美文网首页
设计模式初涉

设计模式初涉

作者: NickYadance | 来源:发表于2017-12-06 23:09 被阅读0次

    对 << Think In Java >> 中的设计范式示例的摘录和理解

    模拟垃圾回收站

    原始模式

    我们的任务很简单,就是把不同类别的垃圾分类,不细加思考的话,很快就能设计出类似于下面的模式,我把它叫做原始模式

    // 收集
    switch((int)(Math.random() * 3)) {
        case 0 :
            bin.addElement(new
                    Aluminum(Math.random() * 100));
            break;
        case 1 :
            bin.addElement(new
                    Paper(Math.random() * 100));
            break;
        case 2 :
            bin.addElement(new
                    Glass(Math.random() * 100));
    }
    Vector glassBin = new Vector(), paperBin = new Vector(), alBin = new Vector();
    Enumeration sorter = bin.elements();
    // 分类
    while(sorter.hasMoreElements()) {
        Object t = sorter.nextElement();
        // RTTI to show class membership:
        if(t instanceof Aluminum)
            alBin.addElement(t);
        if(t instanceof Paper)
            paperBin.addElement(t);
        if(t instanceof Glass)
            glassBin.addElement(t);
    }
    

    这一段是书上的旧版本java代码,switch块模拟随机产生垃圾

    垃圾的结构:

    image

    设计结构我理解为这样:

    image

    如果只是应付课程设计,应该足够拿到学分~~。但观察一下,从图中很容易发现两个问题

    1. 收集过程Trash子类被上溯造型以使容器容纳,分类过程又回过头重新下溯到原来的类型,这样似乎很笨拙
    2. 设想需要增加多种Trash,程序立马显得难以应付。起码我又得在图上增加无数的箭头

    创建范式

    书中这样介绍:

    Factory 方法的基本原理是我们将创建对象所需的基本信息传递给它,然后返回并等候句柄(已经上溯造型
    至基础类型)作为返回值出现。从这时开始,就可以按多形性的方式对待对象了。因此,我们根本没必要知
    道所创建对象的准确类型是什么

    简单的增加一个Info对象,以承载基本信息

    class Info {
    int type;
    // Must change this to add another type:
    static final int MAX_NUM = 4;
    double data;
    Info(int typeNum, double dat) {
    type = typeNum % MAX_NUM;
    data = dat;
    }}
    

    把烦人的switch语句放到factory里,让它根据不同的参数返回不同的对象。创建的代码变成这样

    for(int i = 0; i < 30; i++)
    bin.addElement(
        Trash.factory(
            new Info(
            (int)(Math.random() * Info.MAX_NUM),
                Math.random() * 100)));
    

    现在的结构看上去是这样。

    image

    Trash的创建由Factory控制,主程序不知道任何创建的细节。尽管从代码上看简化并不大,但现在创建过程关注点集中在Factory,不管有没有对象新增,只要它能够返回可用的对象,则对主程序不会有影响。

    总之Factory将变化的影响隔离出来了,比前一个设计更简单、清爽

    原型范式

    static Trash factory(Info i) {
    switch(i.type) {
        default: // To quiet the compiler
    case 0:
        return new Aluminum(i.data);
    case 1:
        return new Paper(i.data);
    case 2:
        return new Glass(i.data);
    // Two lines here:
    case 3:
        return new Cardboard(i.data);
        }
    }
    

    第一眼看上去,Factory不过是把switch语句放到了一个函数里,事实上也确实是这样。除了提到的好处之外,Factory本身还是逃不过笨拙的创建过程。试试再深入一步:

    将与类型有关的所有信息——包括它的创建过程——都移入代表那种类型的类内部

    public static Trash produce(Info info) {
        for (Class<?> type : trashTypes){
            if (type.getName().contains(
                    info.typeName
            )){
                try {
                    Constructor<?> ctor = type.getConstructor(
                            double.class
                    );
                    return (Trash)ctor.newInstance(
                            new Object[]{info.weight}
                    ) ;
                } catch (InstantiationException |IllegalAccessException
                        |InvocationTargetException |NoSuchMethodException e) {
                    e.printStackTrace();
                    return null ;
                }
            }
        }
    
        // new trash type
        System.out.println("Loading trash type: " + info.typeName);
    
        try {
            trashTypes.add(
                    Class.forName(info.typeName)
            );
        } catch (ClassNotFoundException e){
            // Unknown trash type.
            // Sort it to OTHER
            return new OtherTrash(info.weight);
        }
        return produce(info) ;
    }
    

    这段代码可能有些迷糊,基本想法是根据类名(info.typeName),保存Class句柄,然后使用Java的反射机制产生需要的对象。如果想查阅完整的代码可以去书上看。

    总之一点,这里没有了 switch 语句,factory会根据类名自动产生需要的对象。trashTypes容器起到的正是原型的作用,就跟工厂里的一系列模具一样,在模具上加工就能生产出各种各样的产品,模具和加工正对应这个例子中的输入

    com.chapter.sixteen.trash_collector.Glass:54

    现在结构图是这样:

    image

    抽象的应用

    创建的过程已经做得很好了,但讨厌的 if 语句还是一直伴随着我们

    如果必须做不雅的事情,至少应将其本地化到一个类里

    再深入思考,可以得到这样的设计

    image

    再次,将分类的细节隐藏到bins里,从实现上要解决一个问题

    用静态方式编码的方法如何应付一种新类型加入的事实

    也就是说bin要从1.不同的trash中收集2.对应的trash。解决前者很容易,java的继承已经做到了。后者则又需要我们把Class对象拿出来使用了,即RTTI(运行时类型鉴定)。具体是为每一个bin保存一个class对象,在分类时遍历bins里面的bin,放入对应的bin里面:

    image

    尾声

    那么到最后,程序的结构是这样

    image

    只需要在init中增添一个配置,就可以使produce产生不同的结果(装着不同垃圾的垃圾桶:) )其他的行为都不会受到影响

    此中有真意,欲辨已忘言

    相关文章

      网友评论

          本文标题:设计模式初涉

          本文链接:https://www.haomeiwen.com/subject/dqalixtx.html