1 Design Pattern for Open Class (DRAFT) Tanaka Akira <[email protected]> A design pattern describes a software architecture which is reusable and exten- sible. [GoF] is the book containing 23 design patterns for OOP languages like C++ and Java. [GoF] describes several trade offs of the patterns. In C++ and Java, a class is defined by only one module. However, there are languages which can define a class by two or more modules: Cecil, MultiJava, AspectJ, MixJuice, CLOS, Ruby, etc. Such class is called "open class" because it is extensible by modules implemented later [Chambers]. For languages with open class, more extensible and/or simpler design patterns exists. We studied design patterns with open class for each GoF design patterns. In this paper, we describe two patterns: the Abstract Factory pattern and the Visitor pattern. 2 Abstract Factory The Abstract Factory pattern is a design pattern for separating class names from the code which generates the object group of related classes for making them configurable. Following figure is the class diagram of the Abstract Factory pattern. Since the example described in [GoF] uses only one concrete factory for each binaries, the required concrete factory is selected statically. The example builds two binaries for Motif and Presentation Manager from a single source code. Two concrete factories are exist for Motif and Presentation Manager. When a binary is built, it is selected which concrete factory is linked. Such static selection can be implemented with the fewer number of classes by open class as following modules. • The module which defines Window and ScrollBar class and their methods which is independent to window systems. • The module which defines methods for the classes which depends on Motif. • The module which defines methods for the classes which depends on Pre- sentation Manager. There are only two classes. Since they are concrete classes, objects can be in- stantiated directly. So, WidgetFactory and it's subclasses become unnecessary. In MixJuice, it can be described as follows. module client extends window_system { .... new Window() .... .... new ScrollBar() .... } 1 AbstractFactory [WidgetFactory] Client createProductA() [createScrollBar()] AbstractProductA createProductB() [Window] [createWindow()] ProductA2 ProductA1 [PMWindow] [MotifWindow] ConcreteFactory1 ConcreteFactory2 [MotifWidgetFactory] [PMWidgetFactory] AbstractProductB createProductA() createProductA() [ScrollBar] [createScrollBar()] [createScrollBar()] createProductB() createProductB() [createWindow()] [createWindow()] ProductB2 ProductB1 [PMScrollBar] [MotifScrollBar] 2 Figure 1: The original Abstract Factory pattern module window_system { define class Window { define abstract Window(); .... } define class ScrollBar { define abstract ScrollBar(); .... } } module motif extends window_system { class Window { Window() {....} .... } class ScrollBar { ScrollBar() {....} .... } } module presentation_manager extends window_system { class Window { Window() {....} .... } class ScrollBar { ScrollBar() {....} .... } } The binary for Motif is built by the three modules: client, window system and motif. The binary for Presentation Manager is built by the three modules: client, window system and presentation manager. Following figure visualize them as a layered class diagram. Open class can be used in this way in many cases because static selection is typical as pointed by [GoF] as follows, p.90, line.12-13, 1.Factories as singletons An application typically needs only one instance of a Concrete- Factory per product family. There are the following advantages the Abstract Factory pattern with open class over usual OOP like C++ and Java. 3 client window_system motif Window Window Window() Window() ... new Window() … …new ScrollBar() ... ScrollBar ScrollBar ScrollBar() ScrollBar() 4 Figure 2: The layered class diagram for the Abstract Factory pattern client window_system presentation_ manager Window Window Window() Window() ... new Window() ¼ ¼new ScrollBar() ... ScrollBar ScrollBar ScrollBar() ScrollBar() 5 Figure 3: The layered class diagram for the Abstract Factory pattern • AbstractFactory and it's subclasses become unnecessary. Each Product class hierarchy is unified to single class. This reduces classes and makes the system simple, understandable and easier to maintain. • The down cast from AbstractProduct to ConcreteProduct becomes un- necessary. This make the system more type safe. • New product can be introduced by a new module without modifying ex- isting modules. 3 Visitor The Visitor pattern is a design pattern for separating the structure of objects and a its traverser. Following figure visualize the class diagram of the Visitor pattern. But it is possible to separate them without the Visitor pattern using open class as follows. Because it is possible that methods for the traverser can be introduced to classes which represent the structure by another module. module element { define abstract class Element {....} define class ConcreteElementA extends Element {....} define class ConcreteElementB extends Element {....} } module traverser extends element { class Element { define abstract void traverse(); } class ConcreteElementA { void traverse() {....} } class ConcreteElementB { void traverse() {....} } } Visitor class and accept methods become unnecessary by open class. This sim- plify the system. Following figure shows the layered class diagram of the above code. However, Visitor class is required for inheriting traversers. When the inher- itance is used for defining traversers, the Visitor pattern can be introduced by another module by open class as follows. In the following example, Concrete- Visitor1a inherits ConcreteVisitor1. module visitor extends element { class Element { define abstract void accept(Visitor v); } class ConcreteElementA { void accept(Visitor v) { v.visit(this); } class ConcreteElementB { void accept(Visitor v) { v.visit(this); } define class Visitor { define abstract void visit(ConcreteElementA elt); define abstract void visit(ConcreteElementB elt); 6 Client Visitor visitConcreteElementA(ConcreteElementA) visitConcreteElementB(ConcreteElementB) ConcreteVisitor1 ConcreteVisitor2 visitConcreteElementA(ConcreteElementA) visitConcreteElementA(ConcreteElementA) visitConcreteElementB(ConcreteElementB) visitConcreteElementB(ConcreteElementB) ObjectStructure Element accept(Visitor) ConcreteElementA ConcreteElementB accept(Visitor v) accept(Visitor v) operationA() operationB() v visitConcreteElementA(this) v visitConcreteElementA(this) 7 Figure 4: The original Visitor pattern element traverser Element Element traverse() ConcreteElementA ConcreteElementA traverse() ConcreteElementB ConcreteElementB traverse() 8 Figure 5: The Visitor pattern without Visitor class element visitor Element Element accept(Visitor) ConcreteElementA ConcreteElementA accept(Visitor) v.visit(this); ConcreteElementB ConcreteElementB accept(Visitor) v.visit(this); Visitor visit(ConcreteElementA) visit(ConcreteElementB) ConcreteVisitor1 visit(ConcreteElementA) visit(ConcreteElementB) ConcreteVisitor1a visit(ConcreteElementA) visit(ConcreteElementB) 9 Figure 6: The Visitor class implementation } define class ConcreteVisitor1 extends Visitor { void visit(ConcreteElementA elt) {....} void visit(ConcreteElementB elt) {....} } define class ConcreteVisitor1a extends ConcreteVisitor1 { void visit(ConcreteElementA elt) {....} void visit(ConcreteElementB elt) {....} } } [GoF] points out the following problem on the Visitor pattern. p.336, line.4, 3.Adding new ConcreteElement classes is hard. The Visitor pattern makes it hard to add new subclasses of Element. Each new Concrete Element gives rise to a new abstract operation on Visitor and a corresponding implementation in every Concrete- Visitor Class. But adding new ConcreteElement classes is easy with open class as folloing figure and follows. module new_concrete_element extends visitor { define class ConcreteElementC extends Element { .... void accept(Visitor v) { v.visit(this); } } class Visitor { define abstract void visit(ConcreteElementC elt); } class ConcreteVisitor1 { void visit(ConcreteElementC elt) {....} } define class ConcreteVisitor1a { void visit(ConcreteElementC elt) {....} } } There are the following advantages on the Visitor pattern with open class over usual OOP like C++ and Java. If a traverser definition doesn't require inheritance, • The Visitor pat- tern becomes unnecessary and the program is simplified. 10 element visitor new_concrete_element Element Element accept(Visitor) ConcreteElementA ConcreteElementA ConcreteElementC accept(Visitor) v.visit(this); accept(Visitor) v.visit(this); ConcreteElementB ConcreteElementB accept(Visitor) v.visit(this); Visitor Visitor visit(ConcreteElementA) visit(ConcreteElementC) visit(ConcreteElementB) ConcreteVisitor1 ConcreteVisitor1 visit(ConcreteElementA) visit(ConcreteElementC) visit(ConcreteElementB) ConcreteVisitor1a ConcreteVisitor1a visit(ConcreteElementA) visit(ConcreteElementC) visit(ConcreteElementB) 11 Figure 7: New ConcreteElement for the Visitor pattern Table 1: the open class effect on each design pattern design pattern classes intro. ext. type safety simp. mechanism Abstract Factory differ. X X p.90 method imp., field add. Builder differ. X method imp., field add. Factory Method differ. X X method imp. Prototype same p.120 method add. Singleton differ. p.128 class method overridden Adapter differ. p.143 interface add. Bridge differ. X X method/field add. Composite none Decorator none Facade differ. X class method add. Flyweight none Proxy
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages13 Page
-
File Size-