Template Method Pattern

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.

Do these fit your needs?

2 Likes

I can see what you are getting and, in some ways this sort of lines up with the post I made here on Abstract methods - #21 by Joanna_Carter

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.

e.g.
Command

  • ObjectCommand
  • ListCommand
    -- ManagedListCommand
    --- AggregateListCommand
    --- AssociationListCommand

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?

Wouldn’t a simple fatalError do?

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.

Relax people :smiley:

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.

In that case, how in the name of everything reasonable, do you implement a hierarchy of types where one type adds to the functionality of another?

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.

@Joanna_Carter What about this (based on your example in Abstract methods - #21 by Joanna_Carter):

protocol Command {
    
    var permitted: Bool { get set }
    var enabled: Bool { get set }
    
    func doExecute() -> Bool
    func doExecuteSuccess()
    func doExecuteFailure()
    func checkBeforeExecute() -> Bool
    func checkAfterExecute() -> Bool
}

extension Command {
    
    func execute() throws -> Bool {
        guard permitted else {
            fatalError("Not permitted")
        }
        guard enabled else {
            fatalError("Disabled")
        }
        guard checkBeforeExecute() else {
            return false
        }
        let success = doExecute()
        if success {
            doExecuteSuccess()
            guard checkAfterExecute() else {
                return false
            }
        } else {
            doExecuteFailure()
        }
        return success
    }
}

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.

OK. Asking the question has stimulated the brain cells. Here goes with a stab at something :

internal protocol InternalMethods
{
  static func beforeExecute(sender: Base)
  
  static func doExecute(sender: Base)
  
  static func afterExecute(sender: Base)
}


public class Base
{
  internal var beforeExecute: ((Base) -> ())?
  
  internal var doExecute: ((Base) -> ())?
  
  internal var afterExecute: ((Base) -> ())?
  
  func execute()
  {
    beforeExecute?(self)
    
    doExecute?(self)
    
    afterExecute?(self)
  }
}


public class Derived : Base, InternalMethods
{
  internal class func beforeExecute(sender: Base)
  {
    print("Derived beforeExecute")
  }
  
  internal class func doExecute(sender: Base)
  {
    
    print("Derived doExecute")
  }
  
  internal class func afterExecute(sender: Base)
  {
    print("Derived afterExecute")
  }
  
  override init()
  {
    super.init()
    
    beforeExecute = type(of: self).beforeExecute
    
    doExecute = type(of: self).doExecute
    
    afterExecute = type(of: self).afterExecute
  }
}


public class FurtherDerived : Derived
{
  override class func beforeExecute(sender: Base)
  {
    super.beforeExecute(sender: sender as! FurtherDerived)
    
    print("FurtherDerived beforeExecute")
  }
  
  override class func doExecute(sender: Base)
  {
    super.doExecute(sender: sender as! FurtherDerived)
    
    print("FurtherDerived doExecute")
  }
  
  override class func afterExecute(sender: Base)
  {
    super.afterExecute(sender: sender as! FurtherDerived)
    
    print("FurtherDerived afterExecute")
  }
}

Hmmm. Interesting. I've got to go out for the evening now but I'll look into that when I get back

In the meanwhile, I'll be interested to see your reactions to my latest brainstorm :sunglasses:

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.

2 Likes

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:

  • A final class holds the shared logic.
  • 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.
  • The “shared logic” class’s constructor accepts a custom implementation of the delegate protocol, and
  • 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.”

12 Likes

That was a quite interesting read Paul, thanks :)!

1 Like

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.

1 Like

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
  }
}