On Mon, Dec 7, 2015 at 11:55 PM, David Owens II via swift-evolution < >>> swift-evolution@swift.org> wrote:
Well, the basic idea of a class cluster is to hide the internal
implementation. The caller of the API is still supposed to get back an
instance of the clustered type (or that conforms to the interface at least).
It’s still possible to create class clusters though; here’s an example
playground:
private protocol _Cluster {
func description() -> String
}
class Cluster {
private var _instance: _Cluster
init(name: String) {
_instance = _ClusterString(name: name)
}
init(value: Int) {
_instance = _ClusterValue(value: value)
}
func description() -> String {
return _instance.description()
}
}
private class _ClusterString: _Cluster {
private var name: String
init(name: String) { self.name = name }
func description() -> String {
return "_ClusterString: \(name)"
}
}
private class _ClusterValue: _Cluster {
private var value: Int
init(value: Int) { self.value = value }
func description() -> String {
return "_ClusterValue: \(value)"
}
}
let s = Cluster(name: "a string")
s.description()
let v = Cluster(value: 12)
v.description()
The implementation is different from how ObjC implements class
clusters, but the end result is nearly identical in functionality. If Swift
had a form of function redirection, this pattern could be supported with
less boiler-plate. However, I don’t believe this proposal is necessary to
support class clusters.
-David
On Dec 7, 2015, at 12:19 PM, Riley Testut via swift-evolution < >>>> swift-evolution@swift.org> wrote:
Happy Monday everyone!
I wrote up a prototype proposal, which is probably best viewed on
GitHub (
https://github.com/rileytestut/swift-proposals/blob/master/class-cluster.md\).
But for convenience, I’ve included it in this email body as well. Hopefully
someone else thinks this would be an idea worth considering :-)
*## Introduction*
Throughout its frameworks, Apple makes use of the “class cluster”
pattern as a means to separate the public API out from the (potentially
complex) internal representations of the data. Clients of the API simply
use the public API, while under the hood a different implementation is
chosen to most efficiently represent the provided initialization parameter
values.
Unfortunately, because initializers in Swift are not methods like in
Objective-C, there is no way to specify what the actual return value should
be (short of returning nil for failable initializers). This makes it
*impossible* to actually implement the class cluster pattern in Swift.
*## Motivation*
While developing my own Swift framework, I found myself wanting to
provide a similar functionality. For the client of said framework, I wanted
them to be able to create an instance of an essentially abstract class, and
be returned a private subclass instance suited best for handling whatever
input initialization parameters they provided. It didn’t make sense given
the circumstances to ask the user to decide which class would be the best
representation of the data; it should “just work”.
Additionally, the class cluster pattern can make backwards
compatibility significantly easier; instead of littering your code with
branches for different versions of an OS, you could instead have one
if/switch statement to determine the appropriate subclass for the current
OS you’re running on. This allows the developer to trivially keep legacy
code for older platforms while taking advantage of new APIs/designs, and
also without changing *any* client code. An example of the class cluster
pattern being used for this reason can be seen here:
XS-Labs - Blog - 06/18/2013 - Strategies to support the new iOS7 UI
*## Proposed solution*
I propose that we allow for implementation of the class cluster pattern
by providing a way to (at run time) specify the actual type that should be
initialized depending on the provided initialization parameters.
*## Detailed design*
*Introduce a new class method that can return an appropriate type that
should be used for initialization, depending on the provided initialization
parameters.*
This is what I believe to be the most clean solution, and with
(assumedly) minimal impact on the existing nature of Swift’s initialization
process. To ensure this remains safe, the only types allowed to be returned
should be subclasses of the parent class (such as returning a __NSArrayI
for NSArray). Notably, beyond this method, everything else remains the
same; all this does is change what class the initializer is called on
initially.
Here is an ideal implementation gist:
SwiftClassClusterProposal.swift · GitHub
*## Impact on existing code*
There will be zero impact on existing code; if the proposed class
method is not implemented, then it will default to simply initializing the
“base” class, as it always has.
*## Alternatives considered*
*Allow for return values in initializers*
This is essentially how most class cluster patterns are implemented in
Objective-C. Inside the init method, the class inspects the provided
parameters, then assigns self to an instance of the appropriate subclass.
Unfortunately, this is wasteful; memory is allocated for the base class,
and then subsequently replaced with new memory allocated for the
appropriate base class. More importantly though, the whole process can be
complicated; it can be very easy to make an infinite recursive loop by
calling [super init] in the subclass, which then assigns self to a new
instance of the subclass, which then calls [super init]…etc.
tl;dr; this method would work, but would be somewhat inconvenient to
implement.
*Class function to return appropriate instance*
This is probably the simplest approach: simply make a class function
that returns an instance of the appropriate class given a few input
parameters. This totally works, but it means consumers of the API have to
remember to use the class method instead of the initializer. Even if all
initializers for the class were marked private, it would be strange to have
the dissonance between using initializers and class methods to instantiate
types in code. The consumer should not have to know about *any* of the
implementation details; everything should “just work”. Forcing them to use
alternative means to instantiate objects breaks this philosophy, IMO.
*Derive from Objective-C base class*
Another option is to simply derive from an Objective-C base class, and
this is actually what I am doing right now in my framework. Unfortunately,
there is one significant drawback: because the initialization is happening
in Objective-C, you can only provide Objective-C compatible types for the
initialization parameters (so no Swift structs for you!). Additionally,
this (obviously) means whatever code is using it is limited to systems with
Objective-C support, so it is not as portable as a pure-Swift solution.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution