欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

headfirst设计模式(5)—工厂模式体系分析及抽象工厂模式

程序员文章站 2022-03-13 19:19:59
先编一个这么久不写的理由 上周我终于鼓起勇气翻开了headfirst设计模式这本书,看看自己下一个设计模式要写个啥,然后,我终于知道我为啥这么久都没写设计模式了,headfirst的这个抽象工厂模式,额,我看了好几次,都不太理解。 在我的印象中,简单工厂,工厂方法,抽象工厂,这三个东西应该是层层递进 ......

先编一个这么久不写的理由

上周我终于鼓起勇气翻开了headfirst设计模式这本书,看看自己下一个设计模式要写个啥,然后,我终于知道我为啥这么久都没写设计模式了,headfirst的这个抽象工厂模式,额,我看了好几次,都不太理解。

在我的印象中,简单工厂,工厂方法,抽象工厂,这三个东西应该是层层递进的,然后我带着这个思路去看,emmmm,真的没看懂,还好最近又补了一遍《大话设计模式》,揣着刚刚温习了的新知识,然后又上了headfirst这条船,我感觉我这次应该是看懂了。

所以不写并不是因为忙,也不是因为懒,什么上分和吃鸡,我根本都没有听说过,和一个996的码农说这些,表示很扎心。

看完了工厂模式以后,发现后面居然是单例模式,兄弟,你是认真的吗?(泪流满面)

 

抽象工厂举例分析

首先,我去看了一下这本书配套的源代码,嗯,抽象工厂的类,感觉太多太多了,带上测试类一共是35个,这要是贴出来,可以水很大的篇幅了,但是我是那种人吗?当然不是!

碰到这种情况当然是直接给地址,有需要的小伙伴可以自己去看

github地址:https://github.com/bethrobson/head-first-design-patterns

 

工厂模式递进体系分析

先捋一捋书中描述的抽象工厂是个什么意思,要说清楚这个,就必须先说一下它整个工厂模式的叙述的递进关系,具体如下:

1,先拿一个贼简单的创建pizza的简单工厂类pizzastore,来忽悠我,这章很简单,来学学吧,这章源代码只有8个类

2,兴冲冲的看完了第一部分,当然是一鼓作气的看第二部分,第二部分总结起来大概是讲这么一个事情:

  1)我们公司在纽约的那个旗舰店(pizzastore)生意不错,现在我们要开一个分店,名字叫芝加哥披萨店(chicagopizzastore)

  2)分店开了以后,原来的先进经验不能丢啊,所以pizzastore被抽象出来了,搞成了一个抽象类,作为基础技术储备

  3)pizzastore变成了公司的基础部门,专门指导披萨制作的流程

  4)旗舰店没办法啊,一方面自己的名字被占用了,一方面规范管理的风也吹来了,就改名字叫纽约披萨店(nypizzastore),旗下的所有披萨也顺势把名字也改了

  5)新开起来的芝加哥披萨店,倚靠了公司的技术实力,copy了一份旗舰店的菜单,但是也要照顾本地人的口味啊,所以就因地制宜,开发了自己的产品

  整个事情就是这样,涉及到的类有:pizza抽象类(1个),pizzastore抽象类(1个),实体披萨店(2个),实体店的各类pizza(8个),测试类(1个)

  总的来说,这一部分的递进关系很棒,也能很好的体现,简单工厂和工厂方法最大的不同,工厂方法可以对工厂类做到:开闭原则

3,工厂方法扯完了,就来看一下,抽象工厂嘛,这一波操作我当时着实没有看懂,就一直搁置了,为什么当时没看懂呢?

对于正常的抽象工厂的讲解套路来说应该是这个样子的:新增加一个产品线(抽象类,实现类),重新规划一下factory的接口和实现类(factory里新加一个接口,实现类新增实现)

它没有这样搞,它直接根据披萨原料,重新抽象了一套抽象工厂模式的体系出来,不得不说,这个比刚才说的那个新增产品线的例子漂亮太多了,原因有以下几点:

  1)直接新增一个产品线的这种操作,虽然更容易理解,但是会给人一个误导:我在原来factory新增接口的这个操作,是符合设计模式,符合抽象工厂模式的

  2)抽象工厂的缺点是什么?缺点就是,factory的接口,在实际使用的时候,几乎是无法新增产品的,修改太多了。所以它选择了使用原料这套新体系来讲解,更加符合实际

当然,它的例子也不是没有缺点,个人感觉它的缺点有以下几点:

  1)产生了类太多了,35个(上面有说过),对新手不友好,看到这个量级,稍微往后一翻又是一个单例模式,想着这个东西又不怎么能用上,有求生欲望的,都不会在放弃的边缘疯狂试探……

  2)对它本身的工厂方法模式的体系(pizzastore体系),也有很大量的修改,集中体现在各类pizza实现类的缩减(取消了按店铺名称来创建的各类披萨,转而使用了简单工厂模式里面的名字),pizza的方法实现也有很多修改,主要是为了支持原料体系,当然,虽然产生了很多的修改,但是对外部提供的接口是没有影响的,换句话说,虽然实现有了翻天覆地的变化,但是顾客还是无感知的,这个从测试代码就能看出来:

工厂方法模式测试类:

headfirst设计模式(5)—工厂模式体系分析及抽象工厂模式
public class pizzatestdrive {
 
    public static void main(string[] args) {
        pizzastore nystore = new nypizzastore();
        pizzastore chicagostore = new chicagopizzastore();
 
        pizza pizza = nystore.orderpizza("cheese");
        system.out.println("ethan ordered a " + pizza.getname() + "\n");
 
        pizza = chicagostore.orderpizza("cheese");
        system.out.println("joel ordered a " + pizza.getname() + "\n");

        pizza = nystore.orderpizza("clam");
        system.out.println("ethan ordered a " + pizza.getname() + "\n");
 
        pizza = chicagostore.orderpizza("clam");
        system.out.println("joel ordered a " + pizza.getname() + "\n");

        pizza = nystore.orderpizza("pepperoni");
        system.out.println("ethan ordered a " + pizza.getname() + "\n");
 
        pizza = chicagostore.orderpizza("pepperoni");
        system.out.println("joel ordered a " + pizza.getname() + "\n");

        pizza = nystore.orderpizza("veggie");
        system.out.println("ethan ordered a " + pizza.getname() + "\n");
 
        pizza = chicagostore.orderpizza("veggie");
        system.out.println("joel ordered a " + pizza.getname() + "\n");
    }
}
view code

抽象工厂测试类:

headfirst设计模式(5)—工厂模式体系分析及抽象工厂模式
public class pizzatestdrive {
 
    public static void main(string[] args) {
        pizzastore nystore = new nypizzastore();
        pizzastore chicagostore = new chicagopizzastore();
 
        pizza pizza = nystore.orderpizza("cheese");
        system.out.println("ethan ordered a " + pizza + "\n");
 
        pizza = chicagostore.orderpizza("cheese");
        system.out.println("joel ordered a " + pizza + "\n");

        pizza = nystore.orderpizza("clam");
        system.out.println("ethan ordered a " + pizza + "\n");
 
        pizza = chicagostore.orderpizza("clam");
        system.out.println("joel ordered a " + pizza + "\n");

        pizza = nystore.orderpizza("pepperoni");
        system.out.println("ethan ordered a " + pizza + "\n");
 
        pizza = chicagostore.orderpizza("pepperoni");
        system.out.println("joel ordered a " + pizza + "\n");

        pizza = nystore.orderpizza("veggie");
        system.out.println("ethan ordered a " + pizza + "\n");
 
        pizza = chicagostore.orderpizza("veggie");
        system.out.println("joel ordered a " + pizza + "\n");
    }
}
view code

是一个非常赞的地方

 

抽象工厂实现过程

首先,我们从原料的工厂类入手:

public interface pizzaingredientfactory {
 
    dough createdough();
    sauce createsauce();
    cheese createcheese();
    veggies[] createveggies();
    pepperoni createpepperoni();
    clams createclam();
 
}

一共有6种原料可以创建,这个也是它源代码类多的原因之一,由于大多数代码都是相似的,所以这里也就不一一的列举了只列举流程相关的实现

先看酱油(sauce)相关的接口和实现:

public interface sauce {
    string tostring();
}

plumtomatosauce:

headfirst设计模式(5)—工厂模式体系分析及抽象工厂模式
public class plumtomatosauce implements sauce {
    public string tostring() {
        return "tomato sauce with plum tomatoes";
    }
}
view code

marinarasauce:

headfirst设计模式(5)—工厂模式体系分析及抽象工厂模式
public class marinarasauce implements sauce {
    public string tostring() {
        return "marinara sauce";
    }
}
view code

然后看看面团的相关接口和实现:

public interface dough {
    string tostring();
}

thickcrustdough:

headfirst设计模式(5)—工厂模式体系分析及抽象工厂模式
public class thickcrustdough implements dough {
    public string tostring() {
        return "thickcrust style extra thick crust dough";
    }
}
view code

thincrustdough:

headfirst设计模式(5)—工厂模式体系分析及抽象工厂模式
public class thincrustdough implements dough {
    public string tostring() {
        return "thin crust dough";
    }
}
view code

以上,是抽象工厂的基础:产品族的工厂类,产品接口(或者是抽象类),产品的具体实现类请忽略它只是为了方便理解放到一起的

列举了这个,再来看看具体的原料工厂:

chicagopizzaingredientfactory:

headfirst设计模式(5)—工厂模式体系分析及抽象工厂模式
public class chicagopizzaingredientfactory 
    implements pizzaingredientfactory 
{

    //我是面团,我是这里
    public dough createdough() {
        return new thickcrustdough();
    }

    //我是酱油,我在这里
    public sauce createsauce() {
        return new plumtomatosauce();
    }

    public cheese createcheese() {
        return new mozzarellacheese();
    }

    public veggies[] createveggies() {
        veggies veggies[] = { new blackolives(), 
                              new spinach(), 
                              new eggplant() };
        return veggies;
    }

    public pepperoni createpepperoni() {
        return new slicedpepperoni();
    }

    public clams createclam() {
        return new frozenclams();
    }
}
view code

nypizzaingredientfactory:

headfirst设计模式(5)—工厂模式体系分析及抽象工厂模式
public class nypizzaingredientfactory implements pizzaingredientfactory {

    //我是面团,我在这里
    public dough createdough() {
        return new thincrustdough();
    }

    //我是酱油,我在这里
    public sauce createsauce() {
        return new marinarasauce();
    }
 
    public cheese createcheese() {
        return new reggianocheese();
    }
 
    public veggies[] createveggies() {
        veggies veggies[] = { new garlic(), new onion(), new mushroom(), new redpepper() };
        return veggies;
    }
 
    public pepperoni createpepperoni() {
        return new slicedpepperoni();
    }

    public clams createclam() {
        return new freshclams();
    }
}
view code

实现也是很简单的,这里为了不占篇幅就隐藏了,有需要的自己点开看,接下来看一个这个原料体系的建立,需要修改的体现:

1,各类pizza的实现类,需要持有pizzaingredientfactory对象,并且prepare方法会有修改

public class clampizza extends pizza {
    pizzaingredientfactory ingredientfactory;
 
    public clampizza(pizzaingredientfactory ingredientfactory) {
        this.ingredientfactory = ingredientfactory;
    }
 
    void prepare() {
        system.out.println("preparing " + name);
        dough = ingredientfactory.createdough();
        sauce = ingredientfactory.createsauce();
        cheese = ingredientfactory.createcheese();
        clam = ingredientfactory.createclam();
    }
}

2,具体的pizza工厂的修改:

public class chicagopizzastore extends pizzastore {

    protected pizza createpizza(string item) {
        pizza pizza = null;
        //创建各自的原料工厂
        pizzaingredientfactory ingredientfactory =
        new chicagopizzaingredientfactory();

        if (item.equals("cheese")) {
            //创建具体pizza的时候传入原料工厂
            pizza = new cheesepizza(ingredientfactory);
            pizza.setname("chicago style cheese pizza");

        } else if (item.equals("veggie")) {

            pizza = new veggiepizza(ingredientfactory);
            pizza.setname("chicago style veggie pizza");

        } else if (item.equals("clam")) {

            pizza = new clampizza(ingredientfactory);
            pizza.setname("chicago style clam pizza");

        } else if (item.equals("pepperoni")) {

            pizza = new pepperonipizza(ingredientfactory);
            pizza.setname("chicago style pepperoni pizza");

        }
        return pizza;
    }
}

以上就是抽象工厂类的实现套路,以及在head first中,对原工厂方法模式具体影响的叙述,贴代码是不可能贴代码的,这辈子都不可能贴代码的