290 likes | 418 Vues
This chapter discusses how to handle alternatives based on type using polymorphism, enabling the creation of pluggable software components. It highlights the importance of assigning responsibilities correctly to avoid high coupling and low cohesion. The chapter also explores the concept of Pure Fabrication, which helps in managing database operations and intermediate responsibilities through design patterns. Key mechanisms like indirection, encapsulation, and data-driven designs are reviewed to demonstrate how to implement flexibility and stability in software systems.
E N D
Chapter 25 More GRASP Patterns
Polymorphism • Problems: • Handle alternatives based on type – if a program uses if-then-else or case statement conditional logic and a new variation arises, the case logic has to be modified (in many places). • Create pluggable software components – How can you replace one server component with another without affecting the client?
Polymorphism • Solution: When related alternatives or behaviors vary by type (class), assign responsibility for the behavior, using polymorphic operations, to the types for which the behavior varies. • Do not test for the type of an object and use conditional logic to perform varying alternatives based on type. • E.g., in POS application, support third-party tax calculators with adapter objects.
Pure Fabrication • Problem: To which object should a responsibility be assigned when assigning it to a domain layer software class would lead to problems? • E.g., low cohesion, high coupling, low reuse potential. • Solution: Assign a highly cohesive set of responsibilities to an artificial or convenience class that does not represent a problem domain concept.
Pure Fabrication in the POS Case Study • What class should be responsible for saving Sale instances in a relational DB? • Information Expert: Have Sale do it. But: • Requires numerous supporting database-oriented operations unrelated to “sale-ness,” so incohesive • Sale has to be coupled to DB interface. • Many classes need support for saving objects in DB, so code will be duplicated across classes. • Create new class PersistentStorage. • Not a domain concept, but convenient for software developer, and highly cohesive.
Pure Fabrication in the Monopoly Case Study • Dice rolling: Player rolls all dice and sums total. • Summing service is not generalized for use in other games. • Can’t ask for current dice total without rolling again. • Add a Pure Fabrication called Cup to hold dice, roll them, and know their total. • Not a domain concept from Monopoly, but the name of the class is relevant to other games.
Types of Decomposition • Design of objects: Chosen by • Representational decomposition – software class related to/representing a domain object. • Supports low representational gap. • E.g., TableOfContents in the domain of books. • Behavioral decomposition – group by behaviors or by algorithms. • No concern for relating to real-world domain concept. • E.g., TableOfContentsGenerator is an “algorithm” object. • All GoF design patterns are Pure Fabrications.
Indirection • Problem: How do we assign responsibility to avoid direct coupling between two or more classes? • Solution: Assign responsibility to an intermediate object to mediate between other components or services so that they are not directly coupled. • Intermediary creates indirection between other components. • “Most problems in computer science can be solved by another layer of indirection.”
Protected Variations • Problem: How do we design objects, subsystems, and systems so that the variations or instability in these elements does not have an undesirable impact on other elements? • Solution: Identify points of predicted variation or instability; assign responsibility to create a stable interface around them. • E.g., use of polymorphism to implement an adapter for external tax calculators.
Mechanisms Motivated by Protected Variations • Core Protected Variations Mechanisms • Data encapsulation, interfaces, indirection, polymorphism, standards, virtual machines … • Data-Driven Designs • Reading parameters from an external source to change behavior of a system at run time, style sheets, metadata for object-relational mapping, property files, reading in window layouts, . . . • Protects by externalizing the variant.
Mechanisms Motivated by Protected Variations, cont. • Service Lookup • Using naming services or traders to obtain a service (e.g., Java’s JNDI for Jini, or UDDI for web services). • Protected from variations in location of services. • A special case of data-driven design. • Interpreter-Driven Designs • Interpreters that execute rules/scripts read from an external source, neural network or constraint logic networks, . . . • Externalizes the logic, reads, and interprets it.
Mechanisms Motivated by Protected Variations, cont. • Reflective or Meta-Level Designs • Reflective algorithms that use introspection and meta-language services. • Another special case of data-driven design. • Standard Languages • Official language standards protect against a proliferation of varying languages. • E.g., SQL
Mechanisms Motivated by Protected Variations, cont. • Uniform Access • Some languages (Ada, Eiffel, C#) support a syntactic construct (auto getters and setters) so that both a method and field access are expressed the same way. • E.g., aCircle.radius could invoke a radius() method or directly access a public field radius. • Can change from public fields to access methods without changing client code.
Liskov Substitution Principle (LSP) • Software that refers to a type T should work as expected with any substituted implementation or subclass of T. “If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T.” • Formalizes the principle of protection against variations in different implementations of an interface, or subclass extensions of a superclass.
“Don’t Talk to Strangers” (Law of Demeter) • Within a method, messages should only be sent to the following objects: • The this (or self) object. • A parameter of the method. • An attribute of this. • An element of a collection that is an attribute of this. • An object created within the method. • Avoids coupling a client to knowledge of indirect objects.
Don’t Talk to Strangers – An Example • Avoid creating designs that traverse long object structure paths. • Mild violation of the principle: Money amount = sale.getPayment().getTenderedAmount(); • Traversing farther along a structural path: AccountHolder holder = sale.getPayment() .getAccount().getAccountHolder(); • More generally: E someE = foo.getA().getB().getC().getD().getE(); • Preferred: AccountHolder holder = sale.getAccountHolderOfPayment();
Points of Change • Variation point: A point of change in the existing, current system or requirements. • E.g., multiple tax calculator interfaces must be supported. • Evolution point: A speculative point of variation that may arise in the future, but is not present in the existing requirements. • Can be documented with UP Change Cases. • Caution: the cost of engineering protection at evolution points can be higher than reworking a simple design.
Information Hiding • David Parnas, On the Criteria to be Used in Decomposing Systems into Modules: Hide information about the design from other modules, at the points of difficulty or likely change. • The same principle as Protected Variations. • Not simply data encapsulation – that is only one technique to hide information about the design.
Open-Closed Principle (OCP) • Bertrand Meyer: Modules should be both • open (for extension; adaptable) and • closed (to modification that affects clients). • The phrase “closed with respect to X ” means that clients are not affected if Xchanges. • E.g., “the class is closed with respect to instance field definitions” through the mechanism of data encapsulation with private fields and public accessing methods.