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

设计模式之工厂模式(一)

程序员文章站 2022-09-10 14:10:56
工厂模式的学习篇幅比较长,小编第一次看书的时候,就一口气花了一个多小时,还是通读。后面又断断续续地继续了解了下,力争做到清晰的认知,给大家一个简单的学习方式。所以,这次模块分的可能会比之前的多,涉及到多个工厂模式。好的,我们继续冲鸭!!! 除了使用new操作符之外,还有更多制造对象的方法。我们将了解 ......

工厂模式的学习篇幅比较长,小编第一次看书的时候,就一口气花了一个多小时,还是通读。后面又断断续续地继续了解了下,力争做到清晰的认知,给大家一个简单的学习方式。所以,这次模块分的可能会比之前的多,涉及到多个工厂模式。好的,我们继续冲鸭!!!

除了使用new操作符之外,还有更多制造对象的方法。我们将了解到实例化这个活动不应该总是公开地进行,也会认识到初始化经常会造成“耦合”问题。所以,这肯定不是我们希望的这样对吧?继续学习下去,我们将了解工厂模式如何从复杂的依赖中帮你脱困。

当看到“new”,就会想到“具体”

很多朋友应该有一个疑惑,之前说的原则,不应该针对实现编程,但是当我们每次使用new的时候,其实就是在针对实现编程呀。使用了“new”,就是在实例化一个具体类,所以用的的确是实现,而不是接口。遇到一个类的情况,还好说,但是遇到多个类,就必须等到运行时,才知道该实例化哪一个。

duck duck;
if(picnic) {
    duck = new mallardduck();
} else if (hunting) {
    duck = new decoyduck();
} else if (inbathtub) {
    duck = new rubberduck();
}

这段代码如果一旦有变化扩展,就必须重新打开这段代码进行检查和修改,势必会造成部分系统更难维护和更新,也更容易犯错。

“new”有什么不对劲

针对java程序来说,new是最最基础的部分了,所以从技术上来说,new丝毫没有问题,问题的关键在于经常要进行的改变。

针对接口编程,可以隔离掉以后系统可能发生的一大堆改变。为啥呢?如果代码是针对接口编程,那么通过多态可以与任何新类实现该接口。但是,当代码使用大量的具体类时,这就很麻烦了,就必须对代码进行改变。也就是说,你的代码并非“对修改关闭”。想用新的具体类型来扩展代码,就必须重新 打开它。

所以,有没有解决办法呢?还记得我们的第一个原则不,就是用来改变,并帮我们“找出会变化的方面,把它们从不变的部分分离出来”。

之前的装饰者模式,我们喝了可口的咖啡,那么在工厂模式里,就让我们给咖啡加点搭配,来尝尝披萨的口味吧。

识别变化的方面,以及你的初步判断

假设你有一个披萨店,为了让系统有弹性,很是希望这是一个抽象类或接口。但如果这样,这些类或接口就无法直接实例化了。

pizza orderpizza() {
    pizza pizza = new pizza();
    
    pizza.prepare();
    pizza.bake();
    pizza.cut();
    pizza.box();
    return pizza
}

所以,我们还是需要增加一些代码,来“决定”适合披萨的类型,然后再“制造”披萨:

public pizza orderpizza(string type) {
        pizza pizza;
        
        // 根据披萨的类型,我们实例化正确的具体类,然后将其赋值给pizza实例化变量。
        // 请注意,这里的任何披萨都必须实现pizza接口
        if ("cheese".equals(type)) {
            pizza = new cheesepizza();
        } else if ("greek".equals(type)) {
            pizza = new greekpizza();
        } else if ("pepperoni".equals(type)) {
            pizza = new pepperonipizza();
        }
        
        // 一旦我们有了披萨,需要做一些必要的工作。每个pizza的子类型都知道如何准备自己
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }

但是压力来自增加更多的披萨类型

但是,每个产业都会存在竞争对手的,是吧。当其他的披萨店开发出了新产品,你怎么办呢。比如人家有了clam pizza、veggie pizza。还能怎么办,必须与时俱进,与对手一同进步,把这些披萨加入到你的店里,顺带淘汰一些过时了的披萨。

完蛋了,如果要增加披萨,又要去淘汰过时的披萨,在程序的世界里就是实例化某些类,删除某些实例化的类。orderpizza()出问题了,我们无法让orderpizza()对修改关闭;所以,我们用到了第一节学到的封装,我们要封装这些增删改的东西。

封装创建对象的代码

那么封装什么才是符合我们预期的呢,显而易见,现在最好将创建对象移到orderpizza()之外。但怎么做呢?我们要把创建披萨的代码移到另一个对象中,由这个对象专职创建披萨。

if ("cheese".equals(type)) {
    pizza = new cheesepizza();
} else if ("greek".equals(type)) {
    pizza = new greekpizza();
} else if ("pepperoni".equals(type)) {
    pizza = new pepperonipizza();
}

就是把这个移走,新建一个对象,这个新对象只管如何创建披萨。如果任何对象想要创建披萨,直接就找这个即可。

我们称这个新对象为“工厂”,并建造工厂

工厂(factory)处理创建对象的细节,一旦有了simplepizzafactory,orderpizza()就变成此对象的客户。现在,我们方式很简单了,当你想要一个披萨的时候,你就叫披萨工厂去做一个就好了。orderpizza()方法只关心从工厂得到了一个披萨,而这个披萨实现了pizza接口,所以他可以调用prepare()、bake()、cut()、box()来分别进行准备、烘烤、切片、盒装。

那我们把这个工厂建造起来吧,还不赶紧的。

// 这个工厂只做一件事,帮他的客户创建披萨
public class simplepizzafactory {

    // 在工厂内定义一个方法createpizza()方法,所有客户用这个方法来实例化新对象
    public pizza createpizza(string type) {
        pizza pizza = null;

        if (type.equals("cheese")) {
            pizza = new cheesepizza();
        } else if (type.equals("pepperoni")) {
            pizza = new pepperonipizza();
        } else if (type.equals("clam")) {
            pizza = new clampizza();
        } else if (type.equals("veggie")) {
            pizza = new veggiepizza();
        }
        return pizza;
    }
}

虽然这个类还是需要进行频繁的增删改的,还是有点麻烦,但是相比之前呢。为什么这么说,因为这个类,还可以有很多行为,这会儿是创建披萨,那也许是创建菜单呢,又或者是创建饿了么外卖呢,都可以在这个工厂类里创建出来,其他类,只需要调用即可。所以,也就是说,当以后有任何改变,只需要修改这个类即可,省去你在其他地方操作的烦恼。

有了工厂类,其他类的操作就要随之更改了。pizzastore类需要把这个工厂加进来,毕竟我们什么事情都交给工厂去完成了。修改如下:

// 你需要更多的披萨类型传入orderpizza()
    public pizza orderpizza(string type) {
        pizza pizza;
        pizza = factory.createpizza(type);
                
        // 一旦我们有了披萨,需要做一些必要的工作。每个pizza的子类型都知道如何准备自己
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }

定义简单工厂

简单工厂其实不是一个设计模式,反而比较像是一种变成习惯。但由于经常被使用,所以在书中,给他一个“head first pattern荣誉奖”。有些开发人员的确是把这个编程习惯误认为是“工厂模式”(factory pattern)。但是不要因为简单工厂不是一个设计模式,就忽略了他。让我们来看看新的披萨店的类图:
设计模式之工厂模式(一)

好啦,工厂模式的热身结束啦。谢谢简单工厂模式给我们暖身,之后我们会进入两个重量级的模式,他们都是工厂。所以,别担心你吃不到披萨,后面还会有更多的披萨呢。

再提醒一次,在设计模式中,所谓的“实现一个接口”并“不一定”表示“写一个类,并利用implement关键词来实现某个java接口”。“实现一个接口”泛指“实现某个超类型(可以使类或接口)的某个方法”。

简单工厂你get了吗?

推荐阅读:

github地址 headfirstdesign

爱生活,爱学习,爱感悟,爱挨踢

设计模式之工厂模式(一)