设计模式|工厂模式

工厂模式属于创建型模式,它很好的解耦了对象的创建依赖。什么是对象的依赖呢? 在 java 中如果在一个类里new创建了一个对象, 就说明这个类依赖了被创建的对象的类

工厂模式主要关注如何创建对象,在简单工厂模式下我们传入所需要创建类的关键字就可以得到我们需要的对像,在工厂方法中我们可以通过一个个单独的工厂创建不同的对象(一个面包工厂可以生产面包对象,一个披萨工厂可以生产披萨对象),而在抽象工厂中一个工厂具有了多个功能,相比于工厂方法模式它的工厂既可以生产面包对象又可以生产披萨对象。

在自己动手实践后,我们可以清晰的发现,简单工厂就像一个杂货铺,你只要给“物品”贴一个标签(key),就可以在这个“杂货铺”售卖它了,这个杂货铺有什么东西,我们需要自己看有哪些标签(不像工厂方法和抽象工厂,对象的创建有具体的方法和接口),这也就说明通过简单工厂创建对像时他有可能不存在(传入 key 对象可能为 null);对于工厂方法,它是比较专一的工厂——他只生产它相关的对象(面包工厂只专注生产面包,披萨工厂只专注生产披萨);抽象工厂可以认为是一个超级工厂,我既可以生产这个也可以生产哪个,抽象工厂的抽象体现在工厂继承抽象工厂类或实现抽象工厂接口(这个顶层的抽象定义了工厂可以生产的产品),这个“超级工厂”对比简单工厂"杂货铺"并不乱,这方面要归功于接口。

下面让我们结合代码和类的结构来看一下。

简单工厂

类图 在这里插入图片描述 在这个类关系图中 BlackBreak 和 HoneyBread 继承了 BreadMaker,在 BreadFactory 中创建 BlackBread、HoneyBread 对象。这里的 BreadMaker 可以使用接口代替,因为 BreadMaker 只是实现了制作面包的功能,在面向对象中功能最好使用接口来定义,可以更好的解耦依赖。

BreadMaker

public abstract class BreadMaker {
    public abstract void getBread();
}

BlackBread

public class BlackBread extends BreadMaker {
    // 重写
    @Override
    public void getBread() {
        System.out.println("考出黑面包");
    }
}

HoneyBread

public class HoneyBread extends BreadMaker {
    @Override
    public void getBread() {
        System.out.println("烤出蜂蜜面包");
    }
}

BreadFactory

public class BreadFactory {
    public static BreadMaker makeBread(int breadType) {
        switch (breadType) {
            case 0:
                return new BlackBread();
            case 1:
                return new HoneyBread();
            default: {
                // 当用户输入一个不存在的breadType, 工厂不能创建对象, 将返回null对象
                System.out.println("工厂没有这种类型的面包");
                return null;
            }
        }
    }
}

Main 客户端

public class Main {
    public static void main(String[] args) {
        BreadMaker breadMaker;

        breadMaker = BreadFactory.makeBread(0);
        breadMaker.getBread();

        breadMaker = BreadFactory.makeBread(1);
        breadMaker.getBread();
    }
}

output:
考出黑面包
烤出蜂蜜面包

有的人说简单工厂模式并不算一种工厂方法, 这里不反驳也不赞同, 使用一种设计模式最重要的是适合自己业务, 适合是最重要的! 回想想一下 spring 中的 IOC, 通过 key 来创建一个对象或者拿到对象的引用也是很好的设计.

工厂方法

工厂方法 – 我只生产我专注产品! 这是工厂方法的优点, 但是有时候确实没有必要为一个简单的对象创建再写一个工厂类, 这是生产的"产品"简单的时候。

在这里插入图片描述 HoneyBreadFactory 和 BlackBreadFactory 都实现了 IFactory 接口, 二者生产的目标不一样, HoneyBreadFactory 专注于生产蜂蜜面包, 而 BlackBread 专注于生产烤面包. 图中中间部分, BlackBread 和 HoneyBread 继承 BreadMaker 是为了统一接收对象(工厂方法返回对象统一使用 BreadMaker 接收).

IFactory

public interface IFactory {
    BreadMaker creatMaker();
}

HoneyBreadFactory

public class HoneyBreadFactory implements IFactory {
    @Override
    public BreadMaker creatMaker() {
        return new HoneyBread();
    }
}

BlackBreadFactory

public class BlackBreadFactory implements IFactory{
    @Override
    public BreadMaker creatMaker() {
        return new BlackBread();
    }
}

BreadMaker

public abstract class BreadMaker {
    public abstract void getBread();
}

BlackBread

public class BlackBread extends BreadMaker {
    // 重写 这样并不好
    @Override
    public void getBread() {
        System.out.println("考出黑面包");
    }
}

HoneyBread

public class HoneyBread extends BreadMaker {
    @Override
    public void getBread() {
        System.out.println("烤出蜂蜜面包");
    }
}

Main 客户端

public class Main {
    public static void main(String[] args) {
        BreadMaker breadMaker;

        // 黑面包工厂
        BlackBreadFactory blackBreadFactory = new BlackBreadFactory();
        breadMaker = blackBreadFactory.creatMaker();
        breadMaker.getBread();

        // 蜂蜜面包工厂
        HoneyBreadFactory honeyBreadFactory = new HoneyBreadFactory();
        breadMaker = honeyBreadFactory.creatMaker();
        breadMaker.getBread();
    }
}
output:
考出黑面包
烤出蜂蜜面包

抽象工厂一

抽象工厂 – 升级版“超级工厂”, 一个工厂可以生产多种商品。

在这里插入图片描述 IFactory

public interface IFactory {
    BreadMaker creatBreadMaker();
    PizzaMaker creatPizzaMaker();
}

HoneyFactory

public class HoneyFactory implements IFactory {
    @Override
    public BreadMaker creatBreadMaker() {
        return new HoneyBread();
    }

    @Override
    public PizzaMaker creatPizzaMaker() {
        return new HoneyPizza();
    }
}

PizzaMaker

public abstract class PizzaMaker {
    public abstract void getPizza();
}

BlackPizza

public class BlackPizza extends PizzaMaker {
    @Override
    public void getPizza() {
        System.out.println("黑烤披萨做好了");
    }
}

HoneyPizza

public class HoneyPizza extends PizzaMaker {

    @Override
    public void getPizza() {
        System.out.println("蜂蜜披萨做好了");
    }
}

BlackBread、HoneyBread 同 BlackPizza、HoneyPizza 类

Main 客户端

public class Main {
    public static void main(String[] args) {
        HoneyFactory honeyFactory = new HoneyFactory();

        BreadMaker breadMaker;
        breadMaker = honeyFactory.creatBreadMaker();

        PizzaMaker pizzaMaker;
        pizzaMaker = honeyFactory.creatPizzaMaker();

        breadMaker.getBread();
        pizzaMaker.getPizza();
    }
}

抽象工厂二

这里对抽象工厂进一步模拟. 你可以看到抽象工厂如何使得产品的多样化, 以及使用聚合来设计对象的优点.

工厂设定。有两个饮料工厂(河南工厂, 北京工厂), 他们生产的饮料主要有红茶(BlackTea)、百事可乐(PosiCoke)、可口可乐(CocaCoke),虽然他们都生产这三种饮料,但是他们生产的饮料产品配料不同(包装/水/瓶子…). 下面看一下他们如何组织配合的.

在这里插入图片描述

产品 ↓ 在这里插入图片描述

组成饮料的材料 ↓ 在这里插入图片描述

为了方便看,这里贴张包结构图 在这里插入图片描述

这里仅列出主要类的代码

饮料代表类 BlackTea

public class BlackTea implements IDrink {
    private IBottle bottom;
    private ILiquid liquid;
    private IPackaging packaging;

    public IBottle getBottom() {
        return bottom;
    }

    public void setBottom(IBottle bottom) {
        this.bottom = bottom;
    }

    public ILiquid getLiquid() {
        return liquid;
    }

    public void setLiquid(ILiquid liquid) {
        this.liquid = liquid;
    }

    public IPackaging getPackaging() {
        return packaging;
    }

    public void setPackaging(IPackaging packaging) {
        this.packaging = packaging;
    }

    @Override
    public void getDrinkInfo() {
        System.out.println("------Black Tea 红茶------");
        bottom.getBottleInfo();
        liquid.getLiquidInfo();
        packaging.getPackagingInfo();
        System.out.println("---------------------------\n");
    }
}

IDrinkFactory

public interface IDrinkFactory {
    BlackTea createBlackTea();
    CocaCoke createCocaCoke();
    PosiCoke createPosiCoke();
}

BeijingDrinkFactory

import com.elltor.designpattern.factory.test.prod.BlackTea;
import com.elltor.designpattern.factory.test.prod.CocaCoke;
import com.elltor.designpattern.factory.test.prod.PosiCoke;
import com.elltor.designpattern.factory.test.prod.material.*;

public class BeiJingDrinkFactory implements IDrinkFactory {

    @Override
    public BlackTea createBlackTea() {
        BlackTea blackTea = new BlackTea();
        blackTea.setBottom(new PlasticBottle());
        blackTea.setLiquid(new NormalLiquid());
        blackTea.setPackaging(new TFBoysPackaging());
        return blackTea;
    }

    @Override
    public CocaCoke createCocaCoke() {
        CocaCoke cocaCoke = new CocaCoke();
        cocaCoke.setBottom(new PlasticBottle());
        cocaCoke.setLiquid(new NormalLiquid());
        cocaCoke.setPackaging(new TFBoysPackaging());
        return cocaCoke;
    }

    @Override
    public PosiCoke createPosiCoke() {
        PosiCoke posiCoke = new PosiCoke();
        posiCoke.setBottom(new PlasticBottle());
        posiCoke.setLiquid(new NormalLiquid());
        posiCoke.setPackaging(new TFBoysPackaging());
        return posiCoke;
    }
}

HeNanDrinkFactory

import com.elltor.designpattern.factory.test.prod.BlackTea;
import com.elltor.designpattern.factory.test.prod.CocaCoke;
import com.elltor.designpattern.factory.test.prod.PosiCoke;
import com.elltor.designpattern.factory.test.prod.material.*;

public class HeNanDrinkFactory implements IDrinkFactory {
    @Override
    public BlackTea createBlackTea() {
        BlackTea blackTea = new BlackTea();
        blackTea.setBottom(new AluminumBottom());
        blackTea.setLiquid(new NongFuSpringLiquid());
        blackTea.setPackaging(new AngelababyPackaging());
        return blackTea;
    }

    @Override
    public CocaCoke createCocaCoke() {
        CocaCoke cocaCoke = new CocaCoke();
        cocaCoke.setBottom(new AluminumBottom());
        cocaCoke.setLiquid(new NongFuSpringLiquid());
        cocaCoke.setPackaging(new AngelababyPackaging());
        return cocaCoke;
    }

    @Override
    public PosiCoke createPosiCoke() {
        PosiCoke posiCoke = new PosiCoke();
        posiCoke.setBottom(new AluminumBottom());
        posiCoke.setLiquid(new NongFuSpringLiquid());
        posiCoke.setPackaging(new AngelababyPackaging());
        return posiCoke;
    }
}

Main 及打印

public class Main {
    public static void main(String[] args) {
        IDrinkFactory beiJingFactory = new BeiJingDrinkFactory();
        IDrinkFactory heNanFactory = new HeNanDrinkFactory();

        System.out.println("顾客购买河南工厂生产的饮料:");
        heNanFactory.createCocaCoke().getDrinkInfo();
        heNanFactory.createPosiCoke().getDrinkInfo();
        heNanFactory.createBlackTea().getDrinkInfo();

        System.out.println("顾客购买北京生产的饮料:");
        beiJingFactory.createCocaCoke().getDrinkInfo();
        beiJingFactory.createPosiCoke().getDrinkInfo();
        beiJingFactory.createBlackTea().getDrinkInfo();

    }
}

output:
顾客购买河南工厂生产的饮料:
--------Coca Coke 可口可乐--------
this is a aluminum bottom.
the water is nong fu sping.
this is Angelababy advocacy packaging
--------------------------------
------Posi Coke 百事可乐------
this is a aluminum bottom.
the water is nong fu sping.
this is Angelababy advocacy packaging
---------------------------

------Black Tea 红茶------
this is a aluminum bottom.
the water is nong fu sping.
this is Angelababy advocacy packaging
---------------------------

顾客购买北京生产的饮料:
--------Coca Coke 可口可乐--------
this is a Plastic bottle
the water is normal water.
this is TFBoys advocacy packaging
--------------------------------
------Posi Coke 百事可乐------
this is a Plastic bottle
the water is normal water.
this is TFBoys advocacy packaging
---------------------------

------Black Tea 红茶------
this is a Plastic bottle
the water is normal water.
this is TFBoys advocacy packaging
---------------------------

到这里, 通过主要的类和打印结果我们可以看到: 每一种饮料通过接口聚合了各种材料.
在这里插入图片描述 然后在工厂创建过程中根据需要放入相应材料, 虽然是一种名称的产品, 但 传入材料的不同也就构成了不同的产品. 在这里插入图片描述 在这里插入图片描述

在客户端进行相同的调用也就打印出了不同的结果. 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

总结

工厂方法模式的适用性

  • 当一个类不知道他所必须创建的对象时, 比如要创建一个白面包, 但是并不知道它的类是 WhiteBread, 这类通常是增加类对系统扩展
  • 当一个类希望由它的子类来指定它所创建的对象时
  • 当类创建对象的职责委托给多个帮助子类, 并且希望那个帮助子类是代理者这一信息的局部化时, 通常这个类是其他类的聚合, 那么这种类的创建最好用工厂方法管理, 如下 BlackTea 的创建管理. 在这里插入图片描述

使用 对于工厂方法的使用要取决于具体的应用. 对比简单工厂和工厂方法, 可以看出对于一些简单的应用, “产品"较少时, 可以使用简单工厂模式, 对于产品比较丰富的应用过多的分支则不利于维护, 这是应当选择工厂方法模式降低维护量; 在有些场景, 类的初始化并不希望使用者知道, 这时使用工厂方法就是一种很好的选择.