I have already contributed to a thread on Abstract Methods but with no apparent interest.
I agree that Swift is intended to be more of a protocol-based language than an "OO" language but, can anyone please tell me how to implement the Template Method design pattern in Swift?
Since Swift seems fixated on avoiding type-based scope in favour of file-based scope, how, oh how, can we implement this extremely useful and (in other languages) well used pattern?
Personally, I feel this pattern is (nowadays) an example of bad design. I see it used mostly to configure a class's behavior, in which case, subclassing is probably the worst solution.
Since Swift has function types, a simple implementation of this pattern could be:
class C {
var a: () -> Void = { print("default a") }
var b: () -> Void = { print("default b") }
func template() {
print("before a")
a()
print("between a and b")
b()
print("after b")
}
}
let c = C()
c.a = {
print("a")
}
c.template()
This solution uses configurable properties instead of abstract methods. It achieves basically the same goal, but uses object/function composition, instead of inheritance.
A delegate protocol would be another possible solution that doesn't rely on subclassing.
The problem is that it doesn't satisfy the Template Method pattern as, in Swift, it seems there is no way to enforce implementation of abstract methods in typical class hierarchy.
In such a hierarchy, behaviour is "augmented" at each level and requires the subclass to call the superclass's implementation either before or after the code particular to its level of functionality.
Given…
public class Command
{
internal func beforeExecute()
{
// unknown behaviour at this level
}
internal func doExecute()
{
// unknown behaviour at this level
}
internal func afterExecute()
{
// unknown behaviour at this level
}
// template method
public func execute()
{
beforeExecute()
doExecute()
afterExecute()
}
}
Apart from my solution of optional vars to closures, how do you suggest I propagate the internal methods to a hierarchy and enforce their implementation?
Not to be to blunt, and to quote a famous tennis player …
You cannot be serious!!!
fatalError doesn't provide any means of telling the coder that they have forgotten to override/Implement an important method. It just raises a runtime error that might not get noticed until it is too late.
I continue to be amazed at how incomplete the tools for programming for Apple platforms are. I spent seven years on an absolutely massive series of frameworks for Windows development in C# and never had these kind of restrictions on implementing good, tried and tested, OO design patterns.
It seems that Apple are more than happy for developers to churn out poorly conceived and designed MVCs (Massive View Controllers) but, when it comes to good design practice, "oh sorry, we've changed the rules of programming, you'll just have to make do"
I do wonder what Steve Jobs would make of that attitude?
Right, this is impossible to do in Swift with inheritance. You would have to model everything with protocols to get compile errors for not implementing things.
It's not quite right to blame Apple for Swift's design decisions now. Swift's evolution, while still ultimately decided by a Core Team, is community driven. So it's up to us as a community to steer Swift's direction.
That being said, OOP with classes in Swift has always felt somewhat like a bolt on feature. It's my opinion that if Objective-C interop was not already a thing, Swift might not even have true class based inheritance.
I agree that Swift's "classic" OO support is limited. Inheritance without abstract methods is confusing, as you've noticed. Personally, I think the only reason the language even included inheritance was for Objective-C compatibility.
Most everything in the standard library uses protocols. Keep in mind that protocols are not Java/C# interfaces. While they can't include state, they can do pretty much everything else (require methods/properties/initializers, give default implementations, conditionally add things, create type hierarchies ...)
Protocol hierarchies are the preferred way model to that kind of thing.
protocol Player {
func doesThing()
}
protocol MoreSpecificKindOfPlayer : Player {
func someMoreSpecificMethod()
}
That's not entirely true. You can write protocols that behave much like mixins in other languages. The biggest drawback from how you have to do them in Swift is that you have to manually add the protocol properties into the concrete type.
Here, the supertype Command is a protocol. It requires both the "abstract" methods and the properties needed by the execute method. This execute method can already be provided by the protocol through an extension.
Is this more along the lines of what you need?
Basically, the idea here is to not use classes simply as a means to share code, since protocols can do that. Here, only leaf nodes need to be classes or structs.
I'm not of the opinion that class-based Java-like OOP is the final say on how to structure large programs and I find functional languages very promising, however, there is a distinct advantage that traditional OOP has and that is that it's been extensively used in very large corporations with the most byzantine business requirements, extreme differences in programming skills within a team, etc. Therefore, with traditional OOP, there is a vast array of literature (published and in blog posts) about design patterns, antipatterns, etc. etc., something that just has to be rediscovered in newer paradigms such as POP (which, maybe, has its closest analogue in something like Scala Traits or Haskell typeclasses?). Currently it's not clear what good, scalable best practices are in Swift; maybe we'll need an "Effective Swift" book at some point?
In my team, the members have quite different backgrounds, some have done a lot of Java, others have played with Haskell, etc. Swift is a bit of an oddball in that it doesn't subscribe to either classical OOP nor to fully-featured FP (there are neither abstract classes nor HKTs), so none of the "classical" approaches work well, people will try to implement something the one way or the other and hit limitations. That makes coding awkward sometimes. I guess it would be easier if it was more obvious what the "Swift way" of doing things was, but the community is probably not there yet.
Joanna, you might find some inspiration in Siesta’s mechanism for creating custom Requests. Briefly: there is a type called Request that has some universal methods that should never be overridden, but also a family of methods that are a customization point.
One might be tempted to make that an abstract class, but Siesta instead uses the delegate pattern to do it. The approach in brief:
The methods that would be overridable by subclasses in the template pattern instead live in a separate delegate protocol.
That delegate protocol provides defaults for some behavior via a protocol extension.
The methods that would be abstract in the template pattern have no default implementation in the delegate protocol, which provides the same “must implement” guarantee.
delegates all customizable behavior to that custom implementation, e.g. here.
Where the template pattern would expect abstract method implementations to call super, the delegate approach uses a callback type which it passes to the delegate.
A less wordy summary:
template pattern
delegate pattern
abstract class
owner
final method
method on owner
overridable methods
delegate protocol methods
abstract method
no default impl on delegate protocol
non-abstract, non-final method
default impl on delegate protocol
custom class subclasses the abstract template class
custom type implements the delegate protocol, and is passed to owner’s constructor
custom subclass calls super
delegate protocol method receives as an argument either a ref to its owner or a narrowed callback type
This approach has several advantages (beside the fact that it works in Swift):
It accommodates multiple independent customization axes. You can only override an abstract class along a strict hierarchy, but with this pattern there can be 2+ independently composable delegates.
The callback type reduces coupling between custom code and the core type. In the template pattern, the abstract parent class must expose all its callbacks as protected methods; using this pattern, it can expose only the subsets that make sense for each particular customizable method.
This approach leaves you with more flexibility than an abstract class does to refactor the base type — especially if you use the factory pattern to wrap custom delegates.
In some situations, delegates may be reusable in multiple contexts; a template implementation, however, can only subclass one template class at a time. This one is a bit more esoteric, but matters a lot when it matters.
All of the above is why Cocoa / UIKit have preferred delegates to subclassing, even long before Swift existed. It’s an example of the general principle of “favor composition over inheritance.”
There's quite a lot of backlash against OO and inheritance lately, and I'm a little bit concerned Apple jumps on the hype-train as well.
Looking at Apple Developer Documentation, there seems to be no place for subclassing anymore, and every customization is done with closures.
That might be the spirit of our time, but the credo "composition over inheritance" doesn't make the downsides of the more trendy alternatives disappear. On our platform, there's even an argument that isn't relevant for languages with garbage collection:
A subclass can hardly ever cause a retain cycle, which is a big issue with closures (and a little one with delegation).
Also, I think there's an inherent order of composing mechanisms:
Inheritance > Delegation > Closures
It's very easy to move from left to right, but quite clumsy the other way round.
Network.framework is a C API with some Swift polish on top, so hardly indicative of any large OO hate at Apple.
However, I disagree with you regarding your order of composition. It easier to go from a closure API to a delegate API to a subclass API than the other way, since each layer of indirection makes it hard to manage at a closure level. In the Network example, it seems pretty easy to encapsulate the APIs in delegation or subclasses, so that's probably not the best example either.
+1 on this approach. I discovered such posibility when had had same problem as in the original post. So instead of subclassing and maintaining all that code inside one type, you naturally split implementations, which opens many ways to extend the basic logic and provides type safety.
Well, after much brain scratching, I finally ended up with something that should work for the ensuing hierarchy of classes that will derive from Command. It did involve creating several protocols, which introduce an "abstractness" at various places in the hierarchy :
public protocol NamedCommand
{
var name: String { get }
var text: String { get }
}
public protocol PermissibleCommand
{
var enabled: Bool { get set }
var permitted: Bool { get set }
}
public protocol ContextualCommand
{
func adjustToContext()
}
public protocol NotifyBeforeExecuteCommand
{
var beforeExecuteEvent: Command.BeforeExecuteEvent { get }
}
public protocol ExecutableCommand
{
var doExecute: (() -> Bool) { get }
var doExecuteSuccess: (() -> ()) { get }
var doExecuteFailure: (() -> ()) { get }
func checkBeforeExecute() -> Bool
func checkAfterExecute() -> Bool
}
extension ExecutableCommand where Self : PermissibleCommand & NotifyBeforeExecuteCommand
{
public func execute() throws -> Bool
{
if !permitted
{
throw Command.Error.executeCommandNotPermitted
}
if !enabled
{
throw Command.Error.cannotExecuteDisabledCommand
}
var invocationResult = beforeExecuteEvent.invoke()
if !invocationResult.handled
{
if !checkBeforeExecute()
{
return false
}
invocationResult.success = doExecute()
}
if invocationResult.success
{
doExecuteSuccess()
if !checkAfterExecute()
{
return false
}
}
else
{
doExecuteFailure()
}
return invocationResult.success
}
}
public class Command : NamedCommand & NotifyBeforeExecuteCommand & NotifyPropertyChanged
{
enum Error: String, Swift.Error
{
case executeCommandNotPermitted = "Executing this command is not permitted"
case cannotExecuteDisabledCommand = "Cannot execute a disabled command"
}
public class BeforeExecuteEvent : Event<BeforeExecuteEvent.ClosureType, BeforeExecuteEvent.Args>
{
public typealias ClosureType = MVP.ClosureType<Command, Args, EventClosure<Command, Args>>
public class Args : EventArgs
{
public var handled: Bool = false
public var success: Bool = false
}
func invoke() -> (handled: Bool, success: Bool)
{
let args = Args()
invoke(with: args)
return (args.handled, args.success)
}
}
public var name: String = ""
public var text: String = ""
public var enabled: Bool = true
{
didSet
{
if enabled != oldValue
{
onPropertyChanged(keyPath: \Command.enabled)
}
}
}
public var permitted: Bool = true
{
didSet
{
if permitted != oldValue
{
onPropertyChanged(keyPath: \Command.permitted)
}
}
}
public lazy var beforeExecuteEvent: BeforeExecuteEvent =
{
return BeforeExecuteEvent(sender: self)
}()
// MARK: - NotifyPropertyChanged
public lazy var propertyChanged: PropertyChangedEvent<Command> =
{
return PropertyChangedEvent<Command>(sender: self)
}()
}
extension Command : Hashable
{
public var hashValue: Int
{
return ObjectIdentifier(self).hashValue
}
public static func == (lhs: Command, rhs: Command) -> Bool
{
return lhs === rhs
}
}