设计模式 工厂方法模式和抽象工厂模式

一、前言

在正式的介绍工厂模式和抽象工厂模式之前,我们来先简单的认识下简单工厂,简单工厂其实并不是一个设计模式,反而更像是一种约定俗称的编程习惯。下面就以一个糖葫芦的例子来分析一下吧:

这是一个抽象的糖葫芦类

/**
 * 这是糖葫芦类(类名不好,主要是糖葫芦英文不好翻译)
 */
public abstract class Product {

    //选取原料,包括山楂、橘子、糯米等
    abstract void prepare();
    //穿串
    public void chuanChuan() {
        System.out.println("将选取的原料穿串");
    }
    //熬制调料
    public void createSweetener(){
        System.out.println("熬制白糖调料");
    }
    //浇在串上冷却
    public void castSweetenerToChuan() {
        System.out.println("将调料汁浇在串上并冷却");
    }
}

因为在糖葫芦的制作过程中,穿串、熬制甜料、浇糖过程都是不变的,所以我们创建了一个抽象类,不过这依然符合针对接口编程,不针对实现编程的OO原则,因为这里的接口的含义为广义的超类型

这是一个具体的山楂糖葫芦

/**
 * 这是具体的山楂糖葫芦
 */
public class ShanZhaProduct extends Product{

    @Override
    void prepare() {
        System.out.println("选取精品山楂");       
    }
}

这是一个具体的橘子糖葫芦

/**
 * 这个是具体的橘子糖葫芦
 */
public class OrangeProduct extends Product {

    @Override
    void prepare() {
        System.out.println("选取精品橘子");
    }
}

这是一个具体的糯米糖葫芦

/**
 * 这是具体的糯米糖葫芦
 */
public class RiceProduct extends Product {

    @Override
    void prepare() {
        System.out.println("选取精品糯米,并揉成饭团");
    }
}

这是一个简单工厂

/**
 * 创建一个简单工厂
 */
public class SimpleFactory {

    //可根据客户的不同口味需求,提供不同的糖葫芦
    public Product createProduct(int type) {
        switch (type) {
        case SimpleFactoryTest.SHANZHA:
            return new ShanZhaProduct();//山楂
        case SimpleFactoryTest.ORANGE:
            return new OrangeProduct();//橘子
        case SimpleFactoryTest.RICE:
            return new RiceProduct();//糯米
        default:
            return null;
        }
    }
}

结合下面的店铺类,我们可以看出简单工厂将对象的创建细节封装起来(符合封装变化的OO原则),使对象的具体实例化过程从ProductStore代码中删除,让ProductStore从与具体的糖葫芦的依赖中解脱

这是客户购买糖葫芦的店铺

/**
 * 这是卖糖葫芦的小店
 */
public class ProductStore {

    //持有简单工厂对象
    SimpleFactory factory;

    public ProductStore(SimpleFactory factory) {
        this.factory = factory;
    }

    //购买糖葫芦
    public void buyProduct(int type) {
        Product product = factory.createProduct(type);
        product.prepare();
        product.chuanChuan();
        product.createSweetener();
        product.castSweetenerToChuan();
        System.out.println("制作完成,欢迎您的购买");
    }
}

看完了ProductStore的代码你可能会有一个疑问,问什么简单工厂只负责了对象的创建,为什么没有把具体的制作流程都包含进简单工厂中,直接为店铺生产出一种符合客户要求的糖葫芦呢?其实如果保证制作流程不变的情况下,也可以那么做,虽然违背了要让类尽量保持单一责任的原则吧,不过不推荐那样干,毕竟简单工厂模式目的就是为了封装对象的创建。

我们来测下客户购买糖葫芦

public class SimpleFactoryTest {

    public static final int SHANZHA = 0;//山楂原料
    public static final int ORANGE = 1;//橘子原料
    public static final int RICE = 2;//糯米原料

    public static void main(String[] args) {
        SimpleFactory factory = new SimpleFactory();
        ProductStore productStore = new ProductStore(factory);
        productStore.buyProduct(SHANZHA);
    }
}

最终运行结果

这里写图片描述

到这里我们已经介绍完了简单工厂,相信你已经对于它有了一定的认识,虽然简单工厂的使用简单方便,但却不具备弹性,比如,如果SimpleFactory这个工厂并不能满足我的所有需求,我需要再添加一个工厂SimpleFactory二号,是不是还需要更改ProductStore的代码啊,这就违背了开放-关闭原则,我们应该针对接口编程,而不是针对这个SimpleFactory具体实现进行编程,解决办法可以是定义一个工厂接口,使ProductStore持有抽象引用。不要急,接着往下看吧,其实这种情况工厂方法为我们提供了更好的解决办法。

二、认识工厂方法模式

关于工厂方法模式的定义,我就直接引用了:

工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。 –摘自《Head First 设计模式》

还是以上面的糖葫芦为例,由于糖葫芦卖的非常火爆,所有有好多的店铺想要加盟,但是由于地理位置原因,客户的口味和运输成本的问题,一家工厂已经满足不了我们的全部需求啦,我们现在需要在加盟店本地开建工厂。

这里的需求决定要开建多家工厂,并不是说明工厂方法就得需要多个具体工厂类,只开建一家工厂是完全可以的,看完下面之后,你会发现只有一个具体创建者依然很有用。

这是一个糖葫芦店铺的抽象类

public abstract class ProductStore {

    //购买糖葫芦
    public void buyProduct(String type) {
        Product product = createProduct(type);
        if(product == null){
            System.out.println("当前暂不提供该种类糖葫芦");
            return;
        }
        product.prepare();
        product.chuanChuan();
        product.createSweetener();
        product.castSweetenerToChuan();
        System.out.println("制作完成,欢迎您的购买");
    }
    //将具体的小店的实例化过程推迟到子类
    abstract Product createProduct(String type);
}

抽象的目的,就是为了让子类决定要实例化的类是哪一个,ProductStore类只依赖Product,它并不知道要操作的具体糖葫芦种类是什么,这就让产品的实现从使用中解了耦。createProduct(String type)方法,就是我们上面定义中所说的创建对象的接口。

这是一个抽象的糖葫芦类

public abstract class Product {

    //选取原料,包括山楂、橘子、糯米、哈密瓜、西瓜等
    abstract void prepare();
    //穿串
    public void chuanChuan() {
        System.out.println("将选取的原料穿串");
    }
    //熬制调料
    public void createSweetener(){
        System.out.println("熬制白糖调料");
    }
    //浇在串上冷却
    public void castSweetenerToChuan() {
        System.out.println("将调料汁浇在串上并冷却");
    }
}

这是一个记录具体糖葫芦种类的一个枚举

public enum ProductType {

    ShanZhaType("精品山楂"), OrangeType("精品橘子"), Rice("糯米"), Hamimelon("哈密瓜"), Watermelon("西瓜");

    String type;

    private ProductType(String type) {
        this.type = type;
    }
}

这是北方的一家加盟糖葫芦店

public class NorthProductStore extends ProductStore {

    @Override
    Product createProduct(String type) {
        if(ProductType.ShanZhaType.type.equals(type)){
            return new ShanZhaProduct();
        }
        else if(ProductType.OrangeType.type.equals(type)){
            return new OrangeProduct();
        }
        else if(ProductType.Rice.equals(type)){
            return new RiceProduct();
        }
        return null;
    }
}

是不是看着代码非常眼熟,这不就是我们上面的简单工厂嘛,嘿嘿,如果仔细比较你会发现还是很有区别的:简单工厂中使用的工厂是独立的类,或者是一个工厂接口的具体实现,跟ProductStore类是组合依赖的关系,而这个NorthProductStore类是直接从ProductStore类扩展而来的。

这是具体的山楂糖葫芦

public class ShanZhaProduct extends Product{

    @Override
    void prepare() {
        System.out.println("选取精品山楂");       
    }
}

这个是具体的橘子糖葫芦

public class OrangeProduct extends Product {

    @Override
    void prepare() {
        System.out.println("选取精品橘子");
    }
}

这是具体的糯米糖葫芦

public class RiceProduct extends Product {

    @Override
    void prepare() {
        System.out.println("选取精品糯米,并揉成饭团");
    }
}

即使只有上面这一家加盟店是不是工厂方法模式依然很有用,它让商店和工厂中间解耦,当需要添加工厂时,又不必更改商店代码,符合对扩展开放,对修改关闭。

这是西部地区的一家加盟糖葫芦店

public class WestProductStore extends ProductStore{

    @Override
    Product createProduct(String type) {
        if(ProductType.Hamimelon.type.equals(type)){
            return new HamimelonProduct();
        }
        else if(ProductType.Watermelon.type.endsWith(type)){
            return new WatermelonProduct();
        }
        else if(ProductType.ShanZhaType.type.equals(type)){
            return new ShanZhaProduct();
        }
        return null;
    }
}

这是一个具体的哈密瓜糖葫芦

public class HamimelonProduct extends Product {

    @Override
    void prepare() {
        System.out.println("选取精品哈密瓜");
    }
}

这是一个具体的西瓜糖葫芦

public class WatermelonProduct extends Product {

    @Override
    void prepare() {
        System.out.println("选取精品西瓜");
    }
}

我们来测下客户购买糖葫芦

public class FactoryMethodPatternTest {

    public static void main(String[] args) {
        //这是北方的加盟糖葫芦店
        ProductStore pStoreNorth = new NorthProductStore();
        //购买山楂糖葫芦
        pStoreNorth.buyProduct(ProductType.ShanZhaType.type);

        System.out.println("----------");

        //这是西方的加盟糖葫芦店
        ProductStore pStoreWest = new WestProductStore();
        //购买哈密瓜糖葫芦
        pStoreWest.buyProduct(ProductType.Hamimelon.type);
    }
}

最终运行结果

这里写图片描述

三、认识抽象工厂模式

关于抽象工厂模式的定义,我就直接引用了:

抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。 –摘自《Head First 设计模式》

还是以上面的糖葫芦为例,我们知道在糖葫芦的制作过程中,包括制作糖料这个步骤,现由于加盟店的增多,在制作糖料的原料使用上出现了很多问题,为了加强管理,挽回损失,所以总部决定在加盟店本地建设原料工厂,为加盟店提供专用水,白糖和芝麻。

这是一个糖葫芦店铺的抽象类

public abstract class ProductStore {

    //购买糖葫芦
    public void buyProduct(String type) {
        Product product = createProduct(type);
        if(product == null){
            System.out.println("当前暂不提供该种类糖葫芦");
            return;
        }
        product.prepare();
        product.chuanChuan();
        product.createSweetener();
        product.castSweetenerToChuan();
        System.out.println("制作完成,欢迎您的购买");
    }
    //将具体的小店的实例化过程推迟到子类
    abstract Product createProduct(String type);
}

这是一个抽象的糖葫芦类

public abstract class Product {

    //选取原料,包括山楂、橘子、糯米、哈密瓜、西瓜等
    abstract void prepare();
    //穿串
    public void chuanChuan() {
        System.out.println("将选取的原料穿串");
    }
    //熬制调料
    abstract void createSweetener();
    //浇在串上冷却
    public void castSweetenerToChuan() {
        System.out.println("将调料汁浇在串上并冷却");
    }
}

和上面不同的是,当前类熬制调料方法是抽象方法,因为不同的加盟店可能提供的原料不同,所以熬成的调料也就会有所差异

这是一个记录糖葫芦种类的枚举

public enum ProductType {

    ShanZhaType("精品山楂"), OrangeType("精品橘子"), Rice("糯米"), Hamimelon("哈密瓜"), Watermelon("西瓜");

    String type;

    private ProductType(String type) {
        this.type = type;
    }
}

这是一个抽象工厂

public interface IAbstractFactory {

    //拿到温水
    WaterRawMaterial createWater();
    //获取白糖
    SugarRawMaterial createSugar();
    //获取芝麻
    SesameRawMaterial createSesame();
}

为工厂定义一个接口,这个接口负责创建熬制糖料所需要的全部原料对象。当需要创建产品家族和想让制造的相关产品集合起来时,可以优先选择抽象工厂。

这是一个专门给北方糖葫芦加盟店提供原料的具体工厂

public class NorthFactory implements IAbstractFactory {

    @Override
    public WaterRawMaterial createWater() {
        return new WaterRawMaterial();
    }

    @Override
    public SugarRawMaterial createSugar() {
        return new SugarRawMaterial();
    }

    @Override
    public SesameRawMaterial createSesame() {
        return new SesameRawMaterial();
    }
}

这是原料清水类

public class WaterRawMaterial {

    public WaterRawMaterial() {
        System.out.print("加清水");
    }
}

这是原料白糖类

public class SugarRawMaterial {

    public SugarRawMaterial(){
        System.out.print(" 加白糖  ");
    }
}

这是原料芝麻类

public class SesameRawMaterial {

    public SesameRawMaterial() {
        System.out.println("加芝麻");
    }
}

这是北方的一家具体的加盟店

public class NorthProductStore extends ProductStore {

    //持有一个抽象工厂的引用
    IAbstractFactory factory;

    @Override
    Product createProduct(String type) {
        //因为本例中北方加盟店需要北方工厂,不需要改变了,所以就直接依赖具体类了
        factory = new NorthFactory();
        if(ProductType.ShanZhaType.type.equals(type)){
            return new ShanZhaProduct(factory);
        }
        else if(ProductType.OrangeType.type.equals(type)){
            return new OrangeProduct(factory);
        }
        else if(ProductType.Rice.equals(type)){
            return new RiceProduct(factory);
        }
        return null;
    }
}

这是山楂糖葫芦

public class ShanZhaProduct extends Product{

    IAbstractFactory factory;

    public ShanZhaProduct(IAbstractFactory factory) {
        this.factory = factory;
    }

    @Override
    void prepare() {
        System.out.println("选取精品山楂");       
    }

    @Override
    void createSweetener() {
        factory.createWater();
        factory.createSugar();
        factory.createSesame();
    }
}

让糖葫芦类与具体的原料类解耦。工厂方法模式通过继承,扩展自一个类,让子类决定要实例化的类是哪一个,从而达到解耦目的,而抽象工厂模式,通过对象的组合,即将定义了相关或依赖的对象家族接口,实例化之后,传入针对抽象类型所写的代码中,从而使客户从所使用的实际具体产品中解耦。

这是橘子糖葫芦

public class OrangeProduct extends Product {

    IAbstractFactory factory;

    public OrangeProduct(IAbstractFactory factory) {
        this.factory = factory;
    }

    @Override
    void prepare() {
        System.out.println("选取精品橘子");
    }

    @Override
    void createSweetener() {
        factory.createWater();
        factory.createSugar();
        factory.createSesame();
    }
}

这是糯米糖葫芦

public class RiceProduct extends Product {

    IAbstractFactory factory;

    public RiceProduct(IAbstractFactory factory) {
        this.factory = factory;
    }

    @Override
    void prepare() {
        System.out.println("选取精品糯米,并揉成饭团");
    }

    @Override
    void createSweetener() {
        factory.createWater();
        factory.createSugar();
        factory.createSesame();
    }
}

这个例子只提供了北方糖葫芦加盟店和北方原料工厂,你可以自己轻易扩展出西方糖葫芦加盟店和西方原料工厂类,因为他们都是松耦合的,可以轻易扩展

我们来测下客户购买糖葫芦

public class AbstractFactoryPatternTest {

    public static void main(String[] args) {
        //创建一个北方加盟糖葫芦店
        ProductStore pStore = new NorthProductStore();
        //购买一个橘子糖葫芦
        pStore.buyProduct(ProductType.OrangeType.type);
    }
}

运行结果

这里写图片描述

简单工厂、工厂方法模式,抽象工厂模式源码点击下载

猜你喜欢

转载自blog.csdn.net/MingJieZuo/article/details/79880191