Last updated: 2002-11-18 [Single page version] [Japanese version]
We have made a catalog of design pattern improvement achieved by MixJuice language. This catalog is useful not only for MixJuice programmers, but also AspectJ, CLOS and Ruby programmers. (See the section of For other programming languages for details.)
Here is a table that shows which kind of problems of each design pattern are resolved by the basic programming techniques of MixJuice. (We recommend you read Adapter, Decorator and Visitor.)
Meanings of abbreviations:
I or A (See the section of An improvement of the solution or another solution for details):
Resolved problems (See the section of Categories of resolved problems for details):
Whether mentioned in the GoF book or not:
Used Techniques (See the page of Programming techniques of MixJuice for details):
"I" means it is an improvement of the solution. MixJuice can resolve problems of GoF patterns without changing their class structure. In this case, all merits of GoF patterns are inherited to the improved solutions.
"A" means it is another solution whose class structure is different from the corresponding GoF pattern. There may be merits and demerits compared with the original GoF pattern.
The original GoF pattern can not be introduced to the existing program without modifying the source code.
The original GoF pattern does not support some kind of extensions without modifying the source code.
The original GoF pattern forces some names to be public. (Some of these problems can be resolved by the package/nested-class mechanisms of Java.)
The original GoF pattern requires downcast to a subclass in order to invoke the methods of the subclass.
The class structure of the original GoF pattern is complicated and error prone.
Each solution in this catalog is explained using a "layered class diagram" and a pseudo code of MixJuice.
Layered class diagrams express how programs are extended by difference-based modules of MixJuice.
For more details, please see the proposal of layered class diagram.
This catalog contains quoations from the GoF book, "Design Patterns", Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides.
"Intent" of each design pattern are directly quoted from the GoF book.
Each class diagram has been re-written to adapt to the recent notation of UML.
Many of solutions in this catalog can be applicable to other AOP(aspect-oriented programming) or open-class languages, such as AspectJ, CLOS and Ruby.
Many of the programming techniques of MixJuice have corresponding programming techniques of such languages. Here is a table that shows correspondence between techniques of MixJuice and techniques of AspectJ, CLOS and Ruby.
| Techniques | AspectJ | CLOS | Ruby |
| method-ext | around advice | alias/def | |
| interface-add | declare parents | defmethod | include |
| field-add | introduction | assignment | |
| method-add | introduction | defmethod | dynamic method definition |
| abst-method-add | introduction | useless because dynamic typing | |
| namespace-sep | aspect | package | |
| sep-spec-impl | |||
| impl-selection | aspect selection | require selection | |
Some documents of other languages and systems explains how they make some of Design Patterns unnecessary. See the following pages.
Their programming style is quite different from ours. They do not much use introduction feature; however, we use introduction only.
Comments are welcome. Plese send mail to <akr@m17n.org>.
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.

Abstract method addition to AbstractFactory.

module original {
define abstract class AbstractFactory {
define abstract AbstractProductA createProductA();
define abstract AbstractProductB createProductB();
}
define class ConcreteFactory1 extends AbstractFactory {
AbstractProductA createProductA(){...}
AbstractProductB createProductB(){...}
}
define class ConcreteFactory2 extends AbstractFactory {
AbstractProductA createProductA(){...}
AbstractProductB createProductB(){...}
}
define abstract class AbstractProductA {...}
define class ProductA1 extends AbstractProductA {...}
define class ProductA2 extends AbstractProductA {...}
define abstract class AbstractProductB {...}
define class ProductB1 extends AbstractProductB {...}
define class ProductB2 extends AbstractProductB {...}
define class Client {
AbstractFactory factory;
define void m1(){
AbstractProductA a = factory.createProductA();
AbstractProductB b = factory.createProductB();
}
}
}
module extension extends original {
class AbstractFactory {
define abstract AbstractProductC createProductC();
}
class ConcreteFactory1 {
AbstractProductC createProductC(){...}
}
class ConcreteFactory2 {
AbstractProductC createProductC(){...}
}
define abstract class AbstractProductC {...}
define class ProductC1 extends AbstractProductC {...}
define class ProductC2 extends AbstractProductC {...}
class Client {
define void m2(){
AbstractProductC c = factory.createProductC();
}
}
}
Implementation selection for Product classes.

module products {
define class ProductA {
define abstract ProductA();
...
}
define class ProductB {
define abstract ProductB();
...
}
}
module products.implementation1 extends products {
class ProductA {
ProductA() {...}
...
}
class ProductB {
ProductB() {...}
...
}
}
module products.implementation2 extends products {
class ProductA {
ProductA() {...}
...
}
class ProductB {
ProductB() {...}
...
}
}
module client extends products {
... new ProductA() ...
... new ProductB() ...
}
Separate the construction of a complex object from its representation so that the same construction process can create different representations.

Method addition to Builder and ConcreteBuilder classes.

module original {
define abstract class Builder {
define void buildPartA(){}
define void buildPartB(){}
}
define class ConcreteBuilder extends Builder {
void buildPartA(){...}
void buildPartB(){...}
}
define class Director {
Builder builder;
define void construct(){
builder.buildPartA();
builder.buildPartB();
}
}
}
module extension extends original {
class Builder {
define void buildPartC(){}
}
define class ConcreteBuilder2 extends Builder {
void buildPartA(){...}
void buildPartB(){...}
void buildPartC(){...}
}
class Director {
void construct(){
original();
builder.buildPartC();
}
}
}
Implementation selection for Builder.

module builder {
define class Builder {
// This class has Director's function.
define void construct() { ... }
define void buildPartA() {}
define void buildPartB() {}
define void buildPartC() {}
}
}
module builder.implementation1 extends builder {
class Builder {
void buildPartA() { ... }
void buildPartB() { ... }
void buildPartC() { ... }
}
}
module builder.implementation2 extends builder {
class Builder {
void buildPartA() { ... }
void buildPartB() { ... }
void buildPartC() { ... }
}
}
Define an interface for creating an object, but let subclasses decide which class to instantiated. Factory Method lets a class defer instantiation to subclasses.

Implementation selection for Product and Creator.

module m {
define class Product {
define abstract Product();
}
define class Creator {
define Product product;
define void anOperation() { ... product = new Product(); ... }
}
}
module m.implementation extends m {
class Product {
Product() {...}
define void concreteMethod() {...}
}
class Creator {
define void anotherOperation() {
...
product.concreteMethod(); // no need to cast.
...
}
}
}
Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.

Abstract method addition of "copy()" method to the existing classes.

module original {
define abstract class AbstractProduct {...}
define class ConcreteProduct1 extends AbstractProduct {...}
define class ConcreteProduct2 extends AbstractProduct {...}
}
module extension extends original {
class AbstractProduct {
define abstract AbstractProduct copy();
}
class ConcreteProduct1 {
AbstractProduct copy() {...}
}
class ConcreteProduct2 {
AbstractProduct copy() {...}
}
}
Ensure a class only has one instance, and provide a global point of access to it.

Method extension of static methods solve the problem of static methods, limitation of extensibility. (Note that the current version of MixJuice, version 1.0, does not support method extension of static methods; however the future version will support it.)

module original {
define class C {
define static void operation() {...}
define static int data;
}
}
module extension extends original {
class C {
static void operation() { ... original() ...}
}
}
Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.


Super-interface addition to Adaptee class.
The followings are trade-offs between class adapters, object adapters and this solution. (See also p.142 in the GoF book that explains trade-offs between class and object adapters.)

module library {
define class Adaptee {
define void specificRequest() {...}
}
define class SubAdaptee extends Adaptee {...}
}
module application extends library {
define interface Target {
define void request();
}
class Adaptee implements Target {
void request() { specificRequest(); }
}
}
Decouple an abstraction from its implementation so that the two can vary independently.

Abstract method addition to Abstraction and Implementor.

module original {
define abstract class Abstraction {
Implementor imp;
define void operation() { imp.operationImp(); }
}
define class RefinedAbstraction extends Abstraction {
...
}
define abstract class Implementor {
define abstract void operationImp();
}
define class ConcreteImplementorA extends Implementor {
void operationImp() { ... }
}
define class ConcreteImplementorB extends Implementor {
void operationImp() { ... }
}
}
module extension extends original {
class Abstraction {
define void operation2() { imp.operationImp2(); }
}
class Implementor {
define abstract void operationImp2();
}
class ConcreteImplementorA {
void operationImp2() { ... }
}
class ConcreteImplementorB {
void operationImp2() { ... }
}
}
Implementation selection of Abstraction.
+ Downcasts are no longer needed. - This solution is applicable only if just one implementation of Abstraction is used in a program.

module original {
define class Abstraction {
define void operation() {
... implementationOperation1(); ...
}
define abstract void implementationOperation1();
define abstract void implementationOperation2();
}
define class RefinedAbstractionA extends Abstraction {...}
define class RefinedAbstractionB extends Abstraction {...}
}
module implementation1 extends original {
class Abstraction {
void implementationOperation1() {...}
void implementationOperation2() {...}
}
}
module implementation2 extends original {
class Abstraction {
void implementationOperation1() {...}
void implementationOperation2() {...}
}
}
No significant improvement have been found.
Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

Method addition or abstract method addition to Component and Decorator.

module original {
define abstract class Component {
define abstract void operation();
}
define class ConcreteComponent extends Component {
void operation(){...}
}
define abstract class Decorator extends Component {
Component component;
void operation(){ component.operation(); }
}
define class ConcreteDecorator extends Decorator {
void operation(){...}
}
}
module extension extends original {
class Component {
define abstract void newOperation();
}
class ConcreteComponent {
void newOperation(){...}
}
class Decorator {
void newOperation(){ component.newOperation(); }
}
}
Class extension for Component.

module original {
define class Component {
define void operation();
}
}
module extension1 extends original {
class Component {
void operation(){...}
}
}
module extension2 extends original {
class Component {
void operation(){...}
}
}
Provide a unified interface to a set of interfaces in a subsystem. Facede defines a higher-level interface that makes the subsystem easier to use.

Namespace separation.

module subsystem {
define class C1 { ... }
define class C2 { ... }
...
}
module facade {
define class Facade {
define abstract void easy_operation();
}
}
module facade.implementation extends facade, subsystem {
class Facade {
void easy_operation() { ... }
}
}
No significant improvement have been found.
No significant improvement have been found.
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 it.

Method addition to Handler and ConcreteHandler classes.

module original {
define abstract class Handler {
Handler successor;
define void handleRequestA(){ successor.handleRequestA(); }
}
define class ConcreteHandler1 extends Handler {
void handleRequestA() {...}
}
define class ConcreteHandler2 extends Handler {
void handleRequestA() {...}
}
}
module extension extends original {
class Handler {
define void handleRequestB(){ successor.handleRequestB(); }
}
class ConcreteHandler1 {
void handleRequestB() {...}
}
class ConcreteHandler2 {
void handleRequestB() {...}
}
}
No significant improvement have been found.
Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.

Abstract method addition to AbstractExpression.

module original {
define class Client {
AbstractExpression exp;
Context context;
define void m(){
exp.interpret(context);
}
}
define class Context {...}
define abstract class AbstractExpression {
define abstract void interpret(Context context);
}
define class ExpressionA extends AbstractExpression {
void interpret(Context context){...}
}
define class ExpressionB extends AbstractExpression {
void interpret(Context context){...}
}
}
module extension extends original {
class AbstractExpression {
define abstract void newOperation();
}
class ExpressionA {
void newOperation(){...}
}
class ExpressionB {
void newOperation(){...}
}
}
Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

Namespace separation of ConcreteAggregate and ConcreteIterator.

module aggregate {
define class Aggregate {
define abstract Iterator createIterator();
}
define class Iterator {
define abstract void first();
define abstract void next();
define abstract boolean isDone();
define abstract Object currentItem();
}
define class ConcreteAggregate extends Aggregate {
}
define class ConcreteIterator extends Iterator {
}
}
module aggregate.implementation extends aggregate {
class ConcreteAggregate {
Iterator createIterator() {...}
}
class ConcreteIterator {
// accessible to ConcreteAggregate directly.
void first() {...}
void next() {...}
boolean isDone() {...}
Object currentItem() {...}
}
}
No significant improvement have been found.
Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later.

Namespace separation of Memento.

module m {
define class Originator {
define abstract void setMemento(Memento m);
define abstract Memento createMemento();
}
define class Memento {}
}
module m.implementation extends m {
class Originator {
int state;
void setMemento(Memento m) {...}
Memento createMemento() {...}
}
class Memento {
int state;
}
}
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Class extension for Subject.

module original {
define class Subject {
define void sideEffectOperation() {...}
}
}
module extension extends original {
define abstract class Observer {
define abstract void update();
}
class Subject {
define void attach(Observer o) {...}
define void detach(Observer o) {...}
define void changed() {...}
void sideEffectOperation() {
original();
changed();
}
}
}
Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.

Abstract method addition to State.

module original {
define class Context {
State state;
define void request(){
state.handle();
}
}
define abstract class State {
define abstract void handle();
}
define class ConcreteStateA extends State {
void handle(){...}
}
define class ConcreteStateB extends State {
void handle(){...}
}
}
module extension extends original {
class State {
define abstract void newOperation();
}
class ConcreteStateA {
void newOperation(){...}
}
class ConcreteStateB {
void newOperation(){...}
}
}
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

Implementation selection for Context.

module m {
define class C {
define void contextInterface() {
... algorithmInterface() ...
}
define abstract void algorithmInterface();
}
}
module m.strategyA extends m {
class C { void algorithmInterface() {...} }
}
module m.strategyB extends m {
class C { void algorithmInterface() {...} }
}
No significant improvement have been found.
Represent operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.

Separation of specifications and implementations of ConcreteElement classes. (This improvement is applicable also to another solution described below.)

module element {
define abstract class Element {
define abstract void accept(Visitor visitor);
}
define class ConcreteElementA extends Element {
void accept(Visitor v){ v.visitConcreteElementA(this); }
}
define class ConcreteElementB extends Element {
void accept(Visitor v){ v.visitConcreteElementB(this); }
}
define abstract class Visitor {
define abstract void visitConcreteElementA(ConcreteElementA a);
define abstract void visitConcreteElementB(ConcreteElementB a);
}
}
module element.implementation extends element {
class ConcreteElementA {
int state;
}
class ConcreteElementB {
int state;
}
}
module visitor extends element {
define class ConcreteVisitor extends Visitor {}
}
module visitor.implementation
extends visitor, element.implementation {
class ConcreteVisitor {
void visitConcreteElementA(ConcreteElementA a){
... int x = a.state; ...
}
void visitConcreteElementB(ConcreteElementB b){
... int x = b.state; ...
}
}
}
Abstract method addition to Element and ConcreteElement classes solves the introductionability problem. Abstract method addition to Visitor and ConcreteVisitor classes solves the extensibility problem.

module element {
define abstract class Element {...}
define class ConcreteElementA extends Element {...}
define class ConcreteElementB extends Element {...}
}
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);
}
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) {...}
}
}
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) {...}
}
class ConcreteVisitor1a {
void visit(ConcreteElementC elt) {...}
}
}
Abstract method addition to the existing object structure.

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() {...} }
}