Intent
ProblemOne of the dominant strategies of object-oriented design is the “open-closed principle”. Figure demonstrates how this is routinely achieved - encapsulate interface details in a base class, and bury implementation details in derived classes. Clients can then couple themselves to an interface, and not have to experience the upheaval associated with change: no impact when the number of derived classes changes, and no impact when the implementation of a derived class changes. A generic value of the software community for years has been, “maximize cohesion and minimize coupling”. The object-oriented design approach shown in figure is all about minimizing coupling. Since the client is coupled only to an abstraction (i.e. a useful fiction), and not a particular realization of that abstraction, the client could be said to be practicing “abstract coupling” . an object-oriented variant of the more generic exhortation “minimize coupling”. A more popular characterization of this “abstract coupling” principle is “Program to an interface, not an implementation”. Clients should prefer the “additional level of indirection” that an interface (or an abstract base class) affords. The interface captures the abstraction (i.e. the “useful fiction”) the client wants to exercise, and the implementations of that interface are effectively hidden. The Interface entity could represent either an abstract base class, or the method signature expectations by the client. In the former case, the inheritance hierarchy represents dynamic polymorphism. In the latter case, the Interface entity represents template code in the client and the inheritance hierarchy represents static polymorphism. ExampleA Strategy defines a set of algorithms that can be used interchangeably. Modes of transportation to an airport is an example of a Strategy. Several options exist such as driving one’s own car, taking a taxi, an airport shuttle, a city bus, or a limousine service. For some airports, subways and helicopters are also available as a mode of transportation to the airport. Any of these modes of transportation will get a traveler to the airport, and they can be used interchangeably. The traveler must chose the Strategy based on tradeoffs between cost, convenience, and time. Check list
Rules of thumb
Strategy in DelphiThis session consists of the development of a small application to read and pretty-print XML and CSV files. Along the way, we explain and demonstrate the use of the following patterns: State, Interpreter, Visitor, Strategy, Command, Memento, and Facade. Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. We need to store some information about the document we are looking at. In many patterns, the class that holds information used by the main participants in the pattern is called a context (we could have used one in Interpreter, remember?). In our case, the document class acting as the context, and is pretty simple: 1: TDocument = class(TObject)2: private3: FFileText : TStringList;4: FStrategy : TDocumentStrategy;5:6: function GetText : string;7: procedure SetText(const Value : string); The The first thing we will look at is the 1: procedure TDocument.OpenFile(const FileName : string); To understand why we’ve done this, we need to look at the Strategy pattern. This pattern allows us to define several different algorithms for the same thing, each one in a class of its own, and choose between them by using an object of the relevant class. In our case, we’re interested in hiding the details of the search-and-replace and pretty printing from users our document. 1:2: procedure TDocument.SearchAndReplace(const FindText,ReplaceText : string); As you can see, the implementation of the two methods is deferred to the strategy object. The base strategy class is defined as: 1: TDocumentStrategy = class(TObject)2: private3: FDocument : TDocument;4: protected5: property Document : TDocument read FDocument write FDocument;6: public7: constructor Create(ADocument : TDocument); virtual;8:9: procedure SearchAndReplace(const FindText,ReplaceText : string); virtual; abstract;10: procedure PrettyPrint; virtual; abstract; This is an abstract class, because we want to force descendant classes to implement both the methods. It’s quite common for strategy objects to need to access properties of the context, and indeed that’s what we will need to do. To facilitate this, the constructor takes the document as a parameter. Note the use of the Self Encapsulate Field refactoring so descendent strategies can be declared in other units and still have access to the document, i.e. the document property is declared in the protected section. In the file DocumentStrategy.pas you can see the implementations of the two strategy classes. A look at the two 1: procedure TCsvStrategy.SearchAndReplace(const FindText,ReplaceText : string); As you can see, the two are quite different. While it would be no great hassle for the user of the document to call the Delphi The normal alternative to using Strategy is to subclass the context, in this case our document class. We could have a The class hierarchy can also be difficult to structure, particularly where there is more than one algorithm to consider. If one branch of the hierarchy needs to share an implementation from another branch, as well as one from its own, life gets a bit difficult. You could also get the same behaviour by using case statements or lists of conditionals ( There are some downsides, though. There are an increased number of objects in the system (but in my opinion, they will always be smaller and easier to maintain). The strategy and context can be closely coupled, as in our example. It is possible to remedy this by just passing the needed parameters in the strategy methods. I don’t have a big problem with a certain amount of coupling, and in any case, some is unavoidable in this pattern. In our example, the context (i.e. document) created the strategies as needed, but it is also common for the client to create the relevant one and pass it to the context. So we could get the user interface code to create the strategy, for instance. I have found that I can always refactor that so that the context can create the strategy it needs, but maybe there are situations this is not possible or desirable. In either case, you can see that something else needs knowledge of the strategies in order to choose between them. Variations of the pattern exist.One of the more useful ones is making the context have a default implementation of the algorithm, and only creating a strategy object in certain situations. We could have the document use the Note that this pattern is similar to the Template pattern, which encapsulates one algorithm in a class, and lets subclasses vary certain parts of that one algorithm. An example is a sorting algorithm, where the base class might implement a quicksort, and the subclasses can define the comparison function differently. We’ll see another example in the Command pattern. |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论