[swift-evolution] Proposal: Allow class cluster pattern via dynamic initializer return type

Jacob Bandes-Storch jtbandes at gmail.com
Mon Dec 7 14:59:30 CST 2015


To me it seems a simpler solution would be to allow `return`ing a different
object (or assigning to self) from inside init.

On Mon, Dec 7, 2015 at 12:55 PM David Owens II via swift-evolution <
swift-evolution at 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 at 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:
> http://www.xs-labs.com/en/blog/2013/06/18/ios7-new-ui-strategies/
>
> *## 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:
> https://gist.github.com/rileytestut/0e6e80d3f22b845502e7
>
> *## 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 at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151207/201bdafb/attachment.html>


More information about the swift-evolution mailing list