⬆️ ⬇️

Design patterns, iOS view developer. Part 1. Strategy



Content:



Part 0. Singleton Single

Part 1. Strategy

Part 2. The Observer



Let me remind you that in this series of articles, I am analyzing the book "Design Patterns" by Eric and Elizabeth Freeman. And today we will study the pattern "Strategy". Go.



Where do legs (and wings) grow from



The authors of the book tell us the story of the creation of the SimUDuck application. Let's start with the implementation of the initial state of the application: we have the abstract Duck class and two of its heirs: MallardDuck and RedheadDuck . Immediately we are confronted with the first difficulty: there are no abstract classes in Objective-C and Swift.



We get out of the situation with those tools that are: for Objective-C we declare the usual Duck class (in it will be the default implementations) and add the AbstractDuck protocol to it (in it there will be abstract methods that must be implemented in the heirs). It looks like this:



 // Objective-C @protocol AbstractDuck <NSObject> - (void)display; @end @interface Duck : NSObject - (void)quack; - (void)swim; @end 


Accordingly, the heirs will be as follows:



 // Objective-C @interface MallardDuck : Duck <AbstractDuck> @end @implementation MallardDuck - (void)display { } @end @interface RedheadDuck : Duck <AbstractDuck> @end @implementation RedheadDuck - (void)display { } @end 


In Swift, this is a little easier: the protocol and its extensions are sufficient (in the extension, you can implement some protocol methods by default)



 // Swift protocol Duck { func quack() func swim() func display() } extension Duck { func quack() { } func swim() { } } 


And the heirs:



 // Swift class MallardDuck: Duck { func display() { } } class RedheadDuck: Duck { func display() { } } 


The application develops and the ducks have the opportunity to fly



For this, the corresponding method appears in the Duck parent class. And soon after that, it turns out that there is another heir - RubberDuck . Rubber ducks do not fly, and since the method is added to the parent class, it will be available for rubber ducks. In general: the situation was not simple. With further expansion of the application, there will be difficulties with the support of the flight functions (and not only with it, with the quacking function the same story) and with other types of ducks (wood, for example).



First, the authors of the book propose to solve the problem by putting the functions of flight and quacking into separate interfaces (for Objective-c and Swift-protocols) Flyable and Quackable . But this option is not as good as it seems at first glance. The slightest change in the flight function, which must be applied to all flying ducks, entails the introduction of the same code in many places of the program. So this solution is definitely not suitable.



(referring to the unsuitability of this option, the authors refer to the fact that in interfaces (for us protocols) there are no default implementations, but this is true only for Objective-C, but in Swift the default implementation for flight and quacking could be written in extensions of these protocols and redefine these functions only where necessary and not everywhere)



Well, and besides, one of the main goals of the pattern is to replace the implementation of behaviors at runtime, so the authors propose to make the implementation of behaviors out of the Duck class.



To do this, create the protocols FlyBehavior and QuackBehavior :



 // Objective-C @protocol FlyBehavior <NSObject> - (void)fly; @end @protocol QuackBehavior <NSObject> - (void)quack; @end 


 // Swift protocol FlyBehavior { func fly() } protocol QuackBehavior { func quack() } 


And specific classes that implement these protocols: FlyWithWings and FlyNoWay for FlyBehavior , as well as Quack , Squeak and MuteQuack for QuackBehavior (I'll give an example for FlyWithWings , the rest are implemented in a very similar way):



 // Objective-C @interface FlyWithWings : NSObject <FlyBehavior> @end @implementation FlyWithWings - (void)fly { // fly implementation } @end 


 // Swift class FlyWithWings: FlyBehavior { func fly() { // fly implementation } } 


Delegating our all



Now we, in fact, delegate our behavior to any other class that implements the corresponding interface (protocol). How to tell our duck what its behavior in flight and with quacking should be? Very simply, we add to our class (in Swift - protocol) Duck two properties:



 // Objective-C @property (strong, nonatomic) id<FlyBehavior> flyBehavior; @property (strong, nonatomic) id<QuackBehavior> quackBehavior; 


 // Swift var flyBehavior: FlyBehavior { get set } var quackBehavior: QuackBehavior { get set } 


As you can see, they do not have a specific type defined; it is only determined that this is a class that is signed for the corresponding protocol.



quack fly and quack our parent class (or protocol) Duck with similar ones:



 // Objective-C - (void)performFly { [self.flyBehavior fly]; } - (void)performQuack { [self.quackBehavior quack]; } 


 // Swift func performFly() { flyBehavior.fly() } func performQuack() { quackBehavior.quack() } 


Now our duck simply delegates its behavior to the corresponding behavioral object. How do we set the behavior of each duck? For example, during initialization (example for MallardDuck ):



 // Objective-C - (instancetype)init { self = [super init]; if (self) { self.flyBehavior = [[FlyWithWings alloc] init]; self.quackBehavior = [[Quack alloc] init]; } return self; } 


 // Swift init() { self.flyBehavior = FlyWithWings() self.quackBehavior = Quack() } 


Our pattern is ready :)



Conclusion



In iOS development, the Strategy pattern can be found, for example, in the MVP architecture: in it, the presenter is nothing more than a behavioral object for the view controller (the view controller, as you remember, only informs the presenter about the user's actions, but the logic data processing is determined by the presenter), and vice versa: the controller view is a behavioral object for the presenter (the presenter only says "show data to the user", but how it will be shown is decided by the controller view). You will also find this pattern in VIPER, if, of course, you decide to use it in your application. :)



')

Source: https://habr.com/ru/post/321182/



All Articles