一位有匠心的手艺人

和他的生活方式

[译]设计模式滥用之Hello World

| Comments

设计模式流行之初,我曾在某新闻组里看到一条评论,说有人号称他们试图倾尽23个GoF设计模式去编写一个奇葩程序。不过他们说最终没有成,极其所能也只用到其中20个。他们希望客户叫他们返工,没准能加塞另外3个。

试图用遍所有模式是行不通的,终将以生拼硬凑的设计告终——投机取巧的设计充满百无一用的灵活性。这年头软件太过复杂了。我们不能猜测还要什么,而应该关注于本该要什么。

gamma1

大家刚开始学习设计模式时,很少考虑模式的适用场景,想方设法到处套用,觉得模式用得越多,设计越好。结果产生大量不必要复杂的代码。2

与其说是“运用模式”,不如说是“滥用模式”。有人试图在Hello World程序中套用模式,不可避免地导致滥用。

让我们考察一个示例。这是一个经典问题: 编写一个程序,打印字符串Hello World至标准输出。

编程新手会写出如下代码(Java语言):

1
System.out.println("hello world");

这段代码看起来超乎简单。能否加点设计模式?接下来…

首先,定义两个接口SubjectObserver,加入观察者模式

1
2
3
4
5
6
7
8
9
public interface Subject {
    public void attach(Observer observer);
    public void detach(Observer observer);
    public void notifyObservers();
}

public interface Observer {
    public void update(Subject subject);
}

然后,定义两个类HelloWorldSubjectHelloWorldObserver实现以上两个接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class HelloWorldSubject implements Subject {

    private ArrayList<Observer> observers;
    private String str;

    public HelloWorldSubject() {
        super();

        observers = new ArrayList<Observer>();
    }

    public void attach(Observer observer) {
        observers.add(observer);
    }

    public void detach(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObservers() {
        Iterator<Observer> iter = observers.iterator();

        while (iter.hasNext()) {
            Observer observer = iter.next();
            observer.update(this);
        }
    }

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
        notifyObservers();
    }
}

public class HelloWorldObserver implements Observer {

    public void update(Subject subject) {
        HelloWorldSubject sub = (HelloWorldSubject)subject;
        System.out.println(sub.getStr());
    }

}

接着,再添加一个命令模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public interface Command {
    void execute();
}

public class HelloWorldCommand implements Command {

    private HelloWorldSubject subject;

    public HelloWorldCommand(Subject subject) {
        super();

        this.subject = (HelloWorldSubject)subject;
    }

    public void execute() {
        subject.setStr("hello world");
    }

}

然后添加一个抽象工厂模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public interface AbstractFactory {
    public Subject createSubject();
    public Observer createObserver();
    public Command createCommand(Subject subject);
}

public class HelloWorldFactory implements AbstractFactory {

    public Subject createSubject() {
        return new HelloWorldSubject();
    }

    public Observer createObserver() {
        return new HelloWorldObserver();
    }

    public Command createCommand(Subject subject) {
        return new HelloWorldCommand(subject);
    }
}

再添加一个单例模式即大功告成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class FactoryMakerSingleton {

    private static FactoryMakerSingleton instance = null;
    private AbstractFactory factory;

    private FactoryMakerSingleton() {
        factory = new HelloWorldFactory();
    }

    public static synchronized FactoryMakerSingleton getInstance() {
        if (instance == null) {
            instance = new FactoryMakerSingleton();
        }

        return instance;
    }

    public AbstractFactory getFactory() {
        return factory;
    }
}

最后编写一个主类(用作测试)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class AbuseDesignPatterns {

    public static void main(String[] args) {
        AbstractFactory factory = FactoryMakerSingleton.getInstance().getFactory();

        Subject subject = factory.createSubject();
        subject.attach(factory.createObserver());

        Command command = factory.createCommand(subject);

        command.execute();
    }

}

程序运行输出: Hello World

哇,我们已经在Hello World程序中成功地运用四种模式。(嗯,其实还能添加一个迭代器模式,不过已经引用了内置的迭代器,Java中经常如此)。这必须是一个拿得出手的设计呀。那么问题出在哪儿呢?

  1. 对于一个Hello World程序而言代码过于复杂。
  2. 包含多余的灵活性。
  3. 设计和实现的时间成本是彻彻底底的浪费。
  4. 引发了类膨胀。
  5. 违反了KISS原则。3
  6. 没满足问题的要求。问题目的本在于学习打印标准输出,以上代码却缪以千里。

总结

设计模式应具体场景具体运用,不要为运用而运用。设计模式是构建大型软件的强大工具。应恰到好处,切忌滥用。

这儿下载文中的代码。欢迎来函反馈。如果谁有办法在Hello World程序中堆砌更多设计模式,再好不过了。

参考文章

评论