The first seminar in the mini-seminars periodical sessions I've prepared and lead in my spare time while being employed at Exigen Services. Kudos, guys!
Since these presentations were spare time hobby - I've decided to share them :)
Hopefully someone will find them useful.
The intro is - what designs patters are about, some simple examples and a lots of colorful images.
2. Veicināt atvērtu komunikācijas vidi Identificēt stipras / vājas eksistējošas ekspertīzes puses Efektīva dalīšanas ar interesantu / noderīgu / jaunu informāciju Mini semināru mērķi
11. Each pattern describes a problem which occursoverand over again in our environment, and then describes the core of the solutionto that problem, in such a way that you can use this solution a million timesover, without ever doing it the same way twice Christopher Alexander, Sara Ishikawa, MurraySilverstein, Max Jacobson,Ingrid Fiksdahl-King, and ShlomoAngel.A Pattern Language. Oxford UniversityPress, NewYork, 1977. Design Pattern
13. Encapsulate what varies Favor compositon over inheritance Program to interfaces, not implementations Strive for loosely coupled designs between objects what interact Turtles all the way down
20. Synonyms: Policy Intent: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. Strategy
21. Sample code Example #1: class StrategyExample { public static void main(String[] args) { Context context; // Three contexts following different strategies context = new Context(new ConcreteStrategyMultiply()); int resultA = context.executeStrategy(2, 3); context = new Context(new ConcreteStrategyAdd()); int resultB = context.executeStrategy(resultA, 1); } } Example #2: Code snippet from java.io.File public String getAbsolutePath() { return fs.resolve(this);// NT file system, Unix file system, etc… }
22. JDK java.net.URL – use protocol handlers as strategy to open connection java.io.File – use underlying file system as strategy to manage files Eclipse Almost everything is strategy. Why so? … Code next door
28. Intent: Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.Decorator
32. Chain of Responsibility Intent: Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles.
35. Application: Email spam filtering system. Both HTML and Text formats should be supported. Multiple spam recognition algorithms are present and several could be used simultaneously. Multiple weekly report approaches should be supported: XML, HTML files on filesystem;+ email with report attachment. Pattern puzzle
Tasirtasprakseskasnoved pie design patterniem un kastiekturaktiviizmantoti.
What if changing hat changes our behaviour as well? Knight hat will make us fight and Santa Claus hat will make us give presents?
IntentDefine a family of algorithms, encapsulate each one, and make theminterchangeable.Strategy lets the algorithm vary independently fromclients that use it.Also Known AsPolicyApplicabilityUse the Strategy pattern when· many related classes differ only in their behavior. Strategiesprovide away to configure a class with one of many behaviors.· you need different variants of an algorithm. For example, you mightdefinealgorithms reflecting different space/time trade-offs.Strategiescan be used when these variants are implemented as a classhierarchy ofalgorithms [HO87].· an algorithm uses data that clients shouldn't know about. Use theStrategypattern to avoid exposing complex, algorithm-specific datastructures.· a class defines many behaviors, and these appear as multipleconditionalstatements in its operations. Instead of manyconditionals, move relatedconditional branches into their ownStrategy class.
Why it so difficult to find impressive strategy pattern sample in JDK code?Not defined to be highly extensible? Why it’s so easy to find dramatic strategy pattern samples in Eclipse? What about you system code? How many hats do You have?
IntentConvert the interface of a class into another interface clients expect. Adapterlets classes work together that couldn't otherwise because of incompatibleinterfaces.Also Known AsWrapperApplicabilityUse the Adapter pattern when· you want to use an existing class, and its interface does not match theone you need.· you want to create a reusable class that cooperates with unrelated orunforeseen classes, that is, classes that don't necessarily have compatibleinterfaces.· (object adapter only) you need to use several existing subclasses, but it'simpractical to adapt their interface by subclassing every one. An objectadapter can adapt the interface of its parent class.Participants· Target (Shape)o defines the domain-specific interface that Client uses.· Client (DrawingEditor)o collaborates with objects conforming to the Target interface.· Adaptee (TextView)o defines an existing interface that needs adapting.· Adapter (TextShape)o adapts the interface of Adaptee to the Target interface.Collaborations· Clients call operations on an Adapter instance. In turn, the adapter callsAdaptee operations that carry out the request.ConsequencesClass and object adapters have different trade-offs. A class adapter· adapts Adaptee to Target by committing to a concrete Adapter class. As aconsequence, a class adapter won't work when we want to adapt a class andall its subclasses.· lets Adapter override some of Adaptee's behavior, since Adapter is asubclass of Adaptee.· introduces only one object, and no additional pointer indirection is neededto get to the adaptee.An object adapter· lets a single Adapter work with many Adaptees—that is, the Adaptee itselfand all of its subclasses (if any). The Adapter can also add functionalityto all Adaptees at once.· makes it harder to override Adaptee behavior. It will require subclassingAdaptee and making Adapter refer to the subclass rather than the Adapteeitself.Here are other issues to consider when using the Adapter pattern:1. How much adapting does Adapter do? Adapters vary in the amount of work theydo to adapt Adaptee to the Target interface. There is a spectrum of possiblework, from simple interface conversion—for example, changing the names ofoperations—to supporting an entirely different set of operations. Theamount of work Adapter does depends on how similar the Target interfaceis to Adaptee's.2. Pluggable adapters. A class is more reusable when you minimize theassumptions other classes must make to use it. By building interfaceadaptation into a class, you eliminate the assumption that other classessee the same interface. Put another way, interface adaptation lets usincorporate our class into existing systems that might expect differentinterfaces to the class. ObjectWorks\\Smalltalk [Par90] uses the termpluggable adapter to describe classes with built-in interface adaptation.Consider a TreeDisplay widget that can display tree structures graphically.If this were a special-purpose widget for use in just one application, thenwe might require the objects that it displays to have a specific interface;that is, all must descend from a Tree abstract class. But if we wanted tomake TreeDisplay more reusable (say we wanted to make it part of a toolkitof useful widgets), then that requirement would be unreasonable.Applications will define their own classes for tree structures. Theyshouldn't be forced to use our Tree abstract class. Different treestructures will have different interfaces.In a directory hierarchy, for example, children might be accessed with aGetSubdirectories operation, whereas in an inheritance hierarchy, thecorresponding operation might be called GetSubclasses. A reusableTreeDisplay widget must be able to display both kinds of hierarchies evenif they use different interfaces. In other words, the TreeDisplay shouldhave interface adaptation built into it.We'll look at different ways to build interface adaptation into classesin the Implementation section.3. Using two-way adapters to provide transparency. A potential problem withadapters is that they aren't transparent to all clients. An adapted objectno longer conforms to the Adaptee interface, so it can't be used as iswherever an Adaptee object can. Two-way adapters can provide suchtransparency. Specifically, they're useful when two different clients needto view an object differently.Consider the two-way adapter that integrates Unidraw, a graphical editorframework [VL90], and QOCA, a constraint-solving toolkit [HHMV92]. Bothsystems have classes that represent variables explicitly: Unidraw hasStateVariable, and QOCA has ConstraintVariable. To make Unidraw work withQOCA, ConstraintVariable must be adapted to StateVariable; to let QOCApropagate solutions to Unidraw, StateVariable must be adapted toConstraintVariable.
/** * */package com.exigen.jira.plugins.sr.approval.strategies.utils;import java.util.Collection;import java.util.LinkedList;import java.util.List;import org.apache.log4j.Logger;import com.exigen.jira.plugins.sr.approval.strategies.ApproverResolveStrategy;import com.exigen.jira.plugins.sr.approval.strategies.ApproversResolveException;import com.exigen.jira.plugins.sr.approval.strategies.StrategyContext;import com.opensymphony.user.EntityNotFoundException;import com.opensymphony.user.User;import com.opensymphony.user.UserManager;/** * Adapter to map String (user ids) resolvers to jira user resolvers, thus transforming user ids collections to jira * {@link User} instance collections. * * @author Leonid Maslov */public class ApproverJiraUserAdapter implements ApproverResolveStrategy<User> { /** * logger */protected Logger log = Logger.getLogger(getClass()); /** * delegator */private final ApproverResolveStrategy<String> delegator; /** * jira service to access / find jira users by user identifiers */private UserManager userManager; /** * */public ApproverJiraUserAdapter(ApproverResolveStrategy<String> delegator, UserManager userManager) {this.delegator = delegator;this.userManager = userManager; } /** * gets the underlying user id delegator * * @return the delegator */public ApproverResolveStrategy<String> getDelegator() {return delegator; } /** * gets jira user manager service, used to access / find jira users * * @return the userManager */public UserManager getUserManager() {return userManager; } /** * Processes not found userIds records, default implementation will rethrow {@link EntityNotFoundException} * exception. * * @param userId * @param e * @throws EntityNotFoundException * exception possibly rethrown */protected void processNotFoundRecord(String userId, EntityNotFoundException e) throws EntityNotFoundException {throw e; } /* * (non-Javadoc) * * @see * com.exigen.jira.plugins.sr.approval.strategies.ApproverResolveStrategy#resolve(com.exigen.jira.plugins.sr.approval * .strategies.StrategyContext, java.util.Collection) */public List<User> resolve(StrategyContext context, List<User> resolventChain) throws ApproversResolveException {try {log.debug("Converting to user objects");return toUsers(getDelegator().resolve(context, toStrings(resolventChain))); } catch (EntityNotFoundException e) {throw new ApproversResolveException(e); } } /** * Sets jira service to use to lookup users * * @param userManager * the userManager to set */public void setUserManager(UserManager userManager) {this.userManager = userManager; } /** * Transforms jira user accounts to the collection of the userIds (strings). * * @param data * user object collection * @return user id collection */protected List<String> toStrings(Collection<User> data) { List<String> names = new LinkedList<String>();if (data != null) {for (User u : data) { names.add(u.getName()); } }return names; } /** * Transforms userIds collection to the List of the jira {@link User} accounts. See * {@link #processNotFoundRecord(String, EntityNotFoundException)} which processes all not found userid (will * rethrow exception by default). * * @param data * @return User object collection * @throws EntityNotFoundException * if user Id could not be found, {@link #processNotFoundRecord(String, EntityNotFoundException)} could * decide to rethrow this exception */private List<User> toUsers(Collection<String> data) throws EntityNotFoundException { List<User> users = new LinkedList<User>();if (data != null) {for (String userId : data) {try { users.add(getUserManager().getUser(userId)); } catch (EntityNotFoundException e) { processNotFoundRecord(userId, e); } } }return users; }}
Why it so? Is JDK designed to easily plug existing code in?
IntentAttach additional responsibilities to an object dynamically. Decorators providea flexible alternative to subclassing for extending functionality.Also Known AsWrapperParticipants· Component (VisualComponent)o defines the interface for objects that can have responsibilitiesadded to them dynamically.· ConcreteComponent (TextView)o defines an object to which additional responsibilities can beattached.· Decoratoro maintains a reference to a Component object and defines an interfacethat conforms to Component's interface.· ConcreteDecorator (BorderDecorator, ScrollDecorator)o adds responsibilities to the component.Collaborations· Decorator forwards requests to its Component object. It may optionallyperform additional operations before and after forwarding the request.1. More flexibility than static inheritance. The Decorator pattern providesa more flexible way to add responsibilities to objects than can be had withstatic (multiple) inheritance. With decorators, responsibilities can beadded and removed at run-time simply by attaching and detaching them. Incontrast, inheritance requires creating a new class for each additionalresponsibility (e.g., BorderedScrollableTextView, BorderedTextView).This gives rise to many classes and increases the complexity of a system.Furthermore, providing different Decorator classes for a specificComponent class lets you mix and match responsibilities.Decorators also make it easy to add a property twice. For example, to givea TextView a double border, simply attach two BorderDecorators. Inheritingfrom a Border class twice is error-prone at best.2. Avoids feature-laden classes high up in the hierarchy. Decorator offersa pay-as-you-go approach to adding responsibilities. Instead of trying tosupport all foreseeable features in a complex, customizable class, you candefine a simple class and add functionality incrementally with Decoratorobjects. Functionality can be composed from simple pieces. As a result,an application needn't pay for features it doesn't use. It's also easy todefine new kinds of Decorators independently from the classes of objectsthey extend, even for unforeseen extensions. Extending a complex class tendsto expose details unrelated to the responsibilities you're adding.3. A decorator and its component aren't identical. A decorator acts as atransparent enclosure. But from an object identity point of view, adecorated component is not identical to the component itself. Hence youshouldn't rely on object identity when you use decorators.4. Lots of little objects. A design that uses Decorator often results in systemscomposed of lots of little objects that all look alike. The objects differonly in the way they are interconnected, not in their class or in the valueof their variables. Although these systems are easy to customize by thosewho understand them, they can be hard to learn and debug.ConsequencesThe Decorator pattern has at least two key benefits and two liabilities:
ApplicabilityUse Chain of Responsibility when· more than one object may handle a request, and the handler isn't knownapriori. The handler should be ascertained automatically.· you want to issue a request to one of several objects withoutspecifyingthe receiver explicitly.· the set of objects that can handle a request should be specifieddynamically.Participants· Handler (HelpHandler)o defines an interface for handling requests.o (optional) implements the successor link.· ConcreteHandler (PrintButton, PrintDialog)o handles requests it is responsible for.o can access its successor.o if the ConcreteHandler can handle the request, it does so; otherwiseit forwards the request to its successor.· Cliento initiates the request to a ConcreteHandler object on the chain.Collaborations· When a client issues a request, the request propagates along the chainuntila ConcreteHandler object takes responsibility for handling it.ConsequencesChain of Responsibility has the following benefits and liabilities:1. Reduced coupling.The pattern frees an object from knowing which other objecthandles arequest. An object only has to know that a request will behandled"appropriately." Both the receiver and the sender have noexplicitknowledge of each other, and an object in the chain doesn't havetoknow about the chain's structure.As a result, Chain of Responsibility can simplify objectinterconnections.Instead of objects maintaining references to allcandidate receivers, theykeep a single reference to their successor.2. Added flexibility in assigning responsibilities to objects.Chain ofResponsibility gives you added flexibility indistributingresponsibilities among objects. You can add orchangeresponsibilities for handling a request by adding to orotherwisechanging the chain at run-time. You can combine this withsubclassingto specialize handlers statically.3. Receipt isn't guaranteed.Since a request has no explicit receiver, there'sno guaranteeit'll be handled—the request can fall off the end of thechainwithout ever being handled. A request can also go unhandled whenthechain is not configured properly.