工厂模式

目录

1.工厂方法模式

1.1女娲造人的故事

1.2 工厂方法模式的应用

1.2.1 优点

1.3 工厂方法模式的扩展

1.3.1 缩小为简单工厂模式

1.3.2 升级为多个工厂类

1.3.3 延迟初始化

1.4 最佳实战

2.抽象工厂模式

2.1女娲的失误

2.2 抽象工厂模式的应用

2.2.1 优缺点

2.2.2 使用场景


1.工厂方法模式

1.1女娲造人的故事

东汉《风俗通》讲述了一则神话故事:“开天辟地,未有人民,女娲搏黄土造人”,讲述的过程就是女娲造人的故事。造人的过程大概是这样的:首先,女娲采集黄土捏成人的形状,然后放到八卦炉中烧制成不同肤色的人,最后放到大地中去生长。我们可以用面向对象的思维去思考这个过程,实现的类图如下:

实现的代码如下:

public interface Human {
    /**
     * 肤色
     */
    void getColor();

    /**
     * 人会说话
     */
    void talk();
}

人种类:

public class WhiteHuman implements Human {
    @Override
    public void getColor() {
        System.out.println("白色");
    }

    @Override
    public void talk() {
        System.out.println("白人讲话");
    }
}
public class BlackHuman implements Human {
    @Override
    public void getColor() {
        System.out.println("黑色");
    }

    @Override
    public void talk() {
        System.out.println("黑人讲话");
    }
}
public class YelloHuman implements Human {
    @Override
    public void getColor() {
        System.out.println("黄色");
    }

    @Override
    public void talk() {
        System.out.println("黄种人讲话");
    }
}

八卦炉(工厂类):

public abstract class AbstractHumanFactory {
    public abstract <T extends Human> T createHuman(Class<T> clazz);
}
public class HumanFactory extends AbstractHumanFactory {
    @Override
    public <T extends Human> T createHuman(Class<T> clazz) {
        Human human = null;
        try {
            human = (T) clazz.newInstance();
        } catch (Exception e) {
            System.out.println("人种生成失败");
        }
        return (T) human;
    }
}

女娲类(测试类):

public class NvWa {
    public static void main(String[] args) {
        AbstractHumanFactory humanFactory = new HumanFactory();
        Human whiteHuman = humanFactory.createHuman(WhiteHuman.class);
        whiteHuman.getColor();
        whiteHuman.talk();
        System.out.println("++++++++++++++++++++++++++++++++");
        Human blackHuman = humanFactory.createHuman(BlackHuman.class);
        blackHuman.getColor();
        blackHuman.talk();
        System.out.println("++++++++++++++++++++++++++++++++");
        Human yellowHuman = humanFactory.createHuman(YelloHuman.class);
        yellowHuman.getColor();
        yellowHuman.talk();
    }
}

上面女娲造人的实现过程就是一个工厂方法模式。工厂方法模式使用的频率非常高,其定义为:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

1.2 工厂方法模式的应用

1.2.1 优点

工厂方法模式的主要优点为:

  • 良好的封装性,代码结构清晰。一个对象的创建是有条件的约束的,如一个调用者需要一个具体的产品对象,只需要知道这个产品的类名就可以了,不用知道创建对象的艰辛过程,降低模块间的耦合。
  • 工厂方法模式的扩展性非常优秀,在增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类,就可以完成。
  • 工厂方法模式是典型的解耦框架。产品类的实现如何变化,调用者都不需要关系,只需要关心产品的接口,只要接口保持不变,系统中的上层模块就不需要发生变化。

工厂方法模式是new一个对象的替代品,所以在所有需要生成对象的地方都可以使用,但是需要慎重的考虑是否要增加一个工厂类进行管理,增加代码的复杂性。

1.3 工厂方法模式的扩展

工厂方法模式有很多的扩展,而且与其他模式结合使用为例更大,下面将介绍几种扩展:

1.3.1 缩小为简单工厂模式

一个模块仅仅需要一个工厂类,没有必要把它生产出来,使用静态的方法就可以了。根据这一要求,我们把上例中的AbstractHumanFactory修改一下,类图如图所示:

简单工厂模式的工厂类实现代码如下:

public class HumanSingleFactory {
    public <T extends Human> T createHuman(Class<T> clazz) {
        Human human = null;
        try {
            human = (T) clazz.newInstance();
        } catch (Exception e) {
            System.out.println("人种生成失败");
        }
        return (T) human;
    }
}

该模式是工厂方法模式的弱化,因为比较简单,所以称为简单工厂模式,也叫做静态工厂模式。其缺点是工厂类的扩展比较困难,不符合开闭原则,但它仍然是一个非常实用的设计模式。

1.3.2 升级为多个工厂类

当我们做一个比较复杂的项目时,经常会遇到初始化一个对象很耗费精力的情况,所有的产品类都放到一个工厂方法中进行初始化会使代码结构很不清晰。例如,一个产品类有5个具体的实现,每个实现类的初始化参数都不一样,如果写在一个工厂中,势必会导致该方法巨大无比,那应该怎么办呢?

考虑到需要结构清晰,我们就为每个产品都定义一个创造者,然后由调用者自己去选择与哪个工厂方法关联。我们还是以女娲造人为例,每个人种都有一个八卦炉,分别造成不同的人种,实现类图如下:

 

每个人种(具体的产品类)都对应了一个创建者,每个创建者都独立负责创建对应的产品对象,非常符合单一职责原则。实现的相关代码如下:

public abstract class AbstractHumanFactory {
    public abstract Human createHuman();
}

public class BlackHumanFactory extends AbstractHumanFactory {
    @Override
    public Human createHuman() {
        return new BlackHuman();
    }
}

public class WhiteHumanFactory extends AbstractHumanFactory {
    @Override
    public Human createHuman() {
        return new WhiteHuman();
    }
}


public class YellowHumanFactory extends AbstractHumanFactory {
    @Override
    public Human createHuman() {
        return new YelloHuman();
    }
}

抽象方法中已经不再需要传递相关的参数了,因为每一个具体的工厂都已经非常明确自己的职责:创建自己负责的产品类对象。这样做带来的好处就是创建类的职责清晰,而且结构简单,但是给可扩展性和可维护性带来了一定的影响。如果要创建一个产品类,就要建立一个相应的工厂类,这样就增加了扩展的难度。因为工厂类和产品类的数量相同,维护需要考虑两个对象之间的关系。当然,在复杂的应用中,一般采用多工厂的方法,然后再增加一个协调类,避免调用者与各个子工厂交流,协调类的作用是封装子工厂类,对高层模块提供统一的访问接口。

1.3.3 延迟初始化

延迟初始化是指一个对象被消费完毕后,并不立即释放,工厂类保持其初始状态,等待再次被使用。假设HumanFactory负责人种类对象的创建工作,并且通过HUMAN_MAP 变量来产生一个缓存,对需要再次被使用的对象保留。实例代码如下:

public class HumanFactory {
    private static final Map<String, Human> HUMAN_MAP = new HashMap<>();

    public static synchronized Human createHuman(String type) {
        Human human = null;
        if (HUMAN_MAP.containsKey(type)) {
            human = HUMAN_MAP.get(type);
        } else {
            if (type.equals("black")) {
                human = new BlackHuman();
            } else if (type.equals("yellow")) {
                human = new YelloHuman();
            } else {
                human = new WhiteHuman();
            }
            HUMAN_MAP.put(type, human);
        }
        return human;
    }
}

通过定义一个Map容器,容纳所有产生的对象,如果在Map容器中已经有的对象,则直接取出返回;如果没有,则根据需要类型产生一个对象并放入到Map容器中,以方便下次调用。

延迟加载框架是可以扩展的,例如限制一个人种类的最大实例化数量,可以通过判断Map中已有的对象数量来实现,这样的出来是非常有意义的,例如JDBC数据库连接,都会需要一个MaxConnection最大连接数量,该数量就是内存中最大实例化数量。

1.4 最佳实战

工厂方法模式在项目中使用得非常频繁,以至于很多代码中都包含工厂方法模式。该模式还可以和其他模式(如单例模式、模板方法模式、原型模式等),变化出无穷的优秀设计。这正是软件设计和开发的乐趣所在。

2.抽象工厂模式

2.1女娲的失误

上一节我们讲了女娲造人的故事,人虽然造出来了,但是忘记给人类定义性别了。如果要生产出有性别的人种,我们的工厂类(八卦炉)该如何改造呢?只有一个生产设备,要么生产出来的全都是男性,要么都是女性。于是,女娲就使用了“八卦复制术”把原先的八卦炉变成两个,并且略加修改,就成了女性八卦炉和男性八卦炉。实现的类图如下:

相关代码实现如下:

人类接口:

public interface Human {
    /**
     * 肤色
     */
    void getColor();

    /**
     * 人会说话
     */
    void talk();

    /**
     * 每个人有性别
     */
    void getSex();
}

人种有三个抽象方法,分别为肤色、语言和性别。白色人种、黄色人种和黑色人种抽象类实现代码如下:

public abstract class AbstractWhiteHuman implements Human {
    @Override
    public void getColor() {
        System.out.println("白色人种肤色");
    }

    @Override
    public void talk() {
        System.out.println("我是白种人");
    }
}


public abstract class AbstractYellowHuman implements Human {
    @Override
    public void getColor() {
        System.out.println("黄色人种肤色");
    }

    @Override
    public void talk() {
        System.out.println("我是黄种人");
    }
}


public abstract class AbstractBlackHuman implements Human {
    @Override
    public void getColor() {
        System.out.println("黑色人种肤色");
    }

    @Override
    public void talk() {
        System.out.println("我是黑种人");
    }
}

每个抽象类都有两个实现类,分别实现公共的最细节、最具体的事务:肤色和语言。性别的实现放到具体的子类中:

public class FemaleYellowHuman extends AbstractYellowHuman {
    @Override
    public void getSex() {
        System.out.println("黄人女性");
    }
}


public class MaleYellowHuman extends AbstractYellowHuman {
    @Override
    public void getSex() {
        System.out.println("黄人男性");
    }
}

其他的黑色人种、白色人种的男性和女性代码与此类似,不再重复编码。目前,我们已经把真实世界的人种都定义出来了,剩下的就是怎么制造人类。工厂类(八卦炉)定义如下:

public interface HumanFactory {
    /**
     * 制造黄色人种
     *
     * @return
     */
    Human createYellowHuman();

    /**
     * 制造白色人种
     *
     * @return
     */
    Human createWhiteHuman();

    /**
     * 制造黑色人种
     *
     * @return
     */
    Human createBlackHuman();
}

实现分别制造男性和女性的工厂类:

public class FemaleFactory implements HumanFactory {
    @Override
    public Human createYellowHuman() {
        return new FemaleYellowHuman();
    }

    @Override
    public Human createWhiteHuman() {
        return new FemaleWhiteHuman();
    }

    @Override
    public Human createBlackHuman() {
        return new FemaleBlackHuman();
    }
}

public class MaleFactory implements HumanFactory {
    @Override
    public Human createYellowHuman() {
        return new MaleYellowHuman();
    }

    @Override
    public Human createWhiteHuman() {
        return new MaleWhiteHuman();
    }

    @Override
    public Human createBlackHuman() {
        return new MaleBlackHuman();
    }
}

人种有了,八卦炉也有了,我们来重现一下女娲造人的光景:

public class NvWa {
    public static void main(String[] args) {
       //男性生产线
        HumanFactory maleHumanFactory = new MaleFactory();
        //女性生产线
        HumanFactory femaleHumanFactory = new FemaleFactory();
        //生产线建造完成,开始生产
        Human maleYellowHuman = maleHumanFactory.createYellowHuman();
        Human femaleYellowHuman = femaleHumanFactory.createYellowHuman();
        System.out.println("生产一个黄种人女性");
        femaleYellowHuman.getColor();
        femaleYellowHuman.talk();
        femaleYellowHuman.getSex();
        System.out.println("生产一个黄种人男性");
        maleYellowHuman.getColor();
        maleYellowHuman.getSex();
        maleYellowHuman.talk();
    }
}

在上面的设计中,各个工厂内的生产线的职责非常明确,生产出来的产品可以有耦合关系,要知道世界上黑黄白人种的比例为1:3:6,那这就需要女娲娘娘在烧制的时候要做好比例分配。这就是抽象工厂模式。

抽象工厂模式的定义为:为创建一组相关或者相互依赖的对象提供一个接口,而且无需指定它们的具体类。

2.2 抽象工厂模式的应用

2.2.1 优缺点

优点:

  • 封装性:每个产品的实现类不是高层模块要关心的,只要知道工厂类就能创建出一个对象。
  • 产品族内的约束为非公开状态,例如我们要求每生产一个女性,就同时生产出1.2个男性,这样的生产过程对调用工厂类的高层模块来说是透明的,它不需要知道这个约束,具体的产品族内的约束是在工厂内实现的。

缺点:抽象工厂模式最大的缺点就是产品族的扩展非常困难,每当需要增加一个产品族的时候,需要改动很多类的代码,严重违反了开闭原则。

2.2.2 使用场景

一个对象族(或是一组没有任何关系的对象)有相同的约束,则可以使用抽象工厂模式。比如大家在做软件产品开发的过程中,涉及不同操作系统的约束的时候,都可以考虑使用抽象工厂模式。一个应用需要在不同的操作系统上运行,三个不同操作系统上的软件功能、应用逻辑、UI都应该是非常类似的,唯一不同的是调用不同的工厂方法,由不同的产品类去处理与操作系统交互的信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值