设计模式(工厂模式)

目录

一、概述

1.1 为什么会工厂模式

二、 简单工厂模式

2.1 简单工厂模式的类图

2.2 简单工厂模式示列

2.3 优缺点

三 、工厂方法

3.1 工厂方法模式解决和什么问题

3.2 工厂方法模式实例

3.4 工厂方法模式的优点和缺点

四、 抽象工厂模式

4.1 场景

4.2 抽象工厂模式概念

4.3 来看下具体的实例

4.4 抽象工厂模式的优点和缺点

五、 三种工厂模式之间的关系


一、概述

1.1 为什么会有工厂模式

如果使用new来创建对象,简单的对象来好。如果是复杂的对象,有很多的细节(一些成员变量的初始化),这个时候我们就需要了解对象的细节。其实大多数时候我们只是将这个对象拿来用就可以了,并不需要关注那么多细节。

使用new 创建对象的缺点:

  • 客户端需要了解对象的内部细节。

  • 不符合面向接口编程原则,需要知道具体的实现类的细节。

  • 耦合在客户端,万一对象需要做扩展。100 个客户端使用该对象,100 个客户端都需要做修改。

二、 简单工厂模式

2.1 简单工厂模式的类图

 

  • Api: 接口定义客户端需要的功能
  • ApiImpl1/2: 具体的实现类
  • Factory: 负责创建具体的实现类
  • Client: 客户端,只需要知道 Api。 和 Factory

2.2 简单工厂模式示列

我们用培训课程场景,来示范简单工厂模式。

1 先定义客户端需要的功能API 接口: ICourse 

public interface ICourse {

    /**
     * 课程内容
     */
    public void content();
}

2 具体的两个实现类 JavaCourse  和 PythonCourse 

/**
 * Java课程
 */
public class JavaCourse implements ICourse {
    public void content() {
        System.out.println("java课程");
    }
}

/**
 * Pythod课程
 */
public class PythonCourse implements ICourse {
    public void content() {
        System.out.println("Python课程");
    }
}

3 工厂类  CourseFactory 

/**
 * 创建课程工厂
 */
public class CourseFactory {

    public static ICourse createCourse(int type){
        if (type == 1) {
            return new JavaCourse();
        } else if (type == 2) {
            return new PythonCourse();
        }
        return null;
    }
}

4 客户端使用 Client

public class Client {
    public static void main(String[] args) {
        // 不需要知道实现类的创建细节
        ICourse javaCourse = CourseFactory.createCourse(1);
        javaCourse.content();
        ICourse pythonCourse = CourseFactory.createCourse(2);
        pythonCourse.content();
    }
}

 

2.3 优缺点

优点:(工厂模式解决了哪些问题)

  • 隐藏了创建对象的细节
  • 客户端与具体实现类解耦,符合面向 接口编程。

缺点:

  • 工厂类的职责太多。
  • 不符合单一职责。

怎么理解缺点呢?

后期如果再增加怎得课程,增加 if else 判断。 就需要修改 CourseFactory。 造成CourseFactory 代码越来越臃肿, 不利于维护, 不符合开闭原则

 

三 、工厂方法

3.1 工厂方法模式解决和什么问题

上面谈到简单工厂模式的缺点:

1 工厂类的职责太多。2 不符合单一职责。3 新增复杂类型时候,不符合开闭原则。

工厂方法模式就是用来解决上面的缺点的,并且功能更加强大。

我们先来看下工厂方法模式的定义:

定义一个用于创建对象的抽象类,让子类决定实例化哪一个类,使一个类的实例化延迟到子类。

UML类图

从UML类图中可以看出 产品和工厂是平行的关系, 有几个产品实现类,就有几个工厂类。

Factory: 抽象类,里面定义一个抽象方法。创建具体的产品,延迟到子类实现。

Factory1/2: 具体创建实例的工厂。

Api: 产品接口。

Apilmpl1/2: 具体产品的实现。


思考下,如果现在我们要新增一个产品该怎么做呢?

我们需要新增一个产品实现类 ApiImpl3 和 工厂实现类 Factory3 就可以了,不需要去修改原来的Factory类。这样就解决了简单工厂模式的缺点: 符合开闭原则,单一职责原则。

3.2 工厂方法模式实例

还是以创建课程实例为列子。

课程接口,和课程实现类和简单工厂没有差别。这里就不做冗余的创建。主要看下工厂类的改造。

1 抽象工厂类 CourseFactory
/**
 * 抽象工厂父类
 */
public abstract class CourseFactory {

    /**
     * 工厂方法,具体实例化对象由子类实现
     * @return 具体实例
     */
    protected abstract ICourse createCourse();

    public ICourse getCourse() {
        ICourse course = createCourse();
        System.out.println("设置复杂属性...");
        return course;
    }
}

protected abstract ICourse createCourse(); 延迟到子类实现,创建具体的实例。该方法一般不供外部调用。可以理解为钩子函数。

外部使用 getCourse() 方法。在里面可以进行一些公共的操作。比如 设置一些公共复杂的属性。 

 

2 具体工厂实现类

/**
 * java 课程工厂
 */
public class JavaCourseFactory extends CourseFactory {
    protected ICourse createCourse() {
        return new JavaCourse();
    }
}

/**
 * python 课程工厂
 */
public class PythonCourseFactory extends CourseFactory {
    protected ICourse createCourse() {
        return new PythonCourse();
    }
}

 3 Client端使用

public class Client {
    public static void main(String[] args) {

        CourseFactory javaCourseFactory = new JavaCourseFactory();
        javaCourseFactory.getCourse().content();

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

        CourseFactory pythonCourseFactory = new PythonCourseFactory();
        pythonCourseFactory.getCourse().content();

    }
}

运行结果

 

3.4 工厂方法模式的优点和缺点

优点:

  • 扩展方便,新增产品类。不需要修改工厂方法。符合开闭原则
  • 工厂类符合单一职责原则,维护方便。
  • 适用于创建一些实例化过程比较复杂的对象。

缺点:

  • 类太多,可以看出,工厂方法模式比简单工厂模式 多出了 很多 具体的工厂类。 增加了复杂度。
  • 增加了系统的抽象性和理解难度。

四、 抽象工厂模式

4.1 场景

生活中一个常见的列子,组装电脑。 组装电脑需要 CPU 和 主板。先想一下怎么来组装一台电脑呢,简单一点假设组装电脑只需要CPU 和 主板。首先,我们需要CPU 和 主板,然后再将CPU和主板 交给装机工程师进行组装。怎么生产CPU和主板呢,通过上面简单工厂模式和工厂方法模式的学习,我们很自然的想到了用工厂模式创建,代码如下。

1 cpu 接口 和 cpu实现类

/**
 * CPU
 */
public interface ICPU {

    /**
     * 计算能力
     */
    public void calcuator();
}

public class IntelCPU implements ICPU {

    /**
     * 引脚个数
     */
    private int pins;

    public IntelCPU(int pins) {
        this.pins = pins;
    }

    public void calcuator() {
        System.out.println("IntelCPU 计算能力!引脚个数:" + this.pins);
    }
}

/**
 * AMDcpu
 */
public class AmdCPU implements ICPU {

    private int pins;

    public AmdCPU(int pins) {
        this.pins = pins;
    }

    public void calcuator() {
        System.out.println("AmdCpu 计算能力!引脚个数:" + this.pins);
    }
}

2 主板 和 主板实现类

/**
 * 主板
 */
public interface IMainboard {

    /**
     * 示意方法,安装cpu
     */
    public void installCPU();
}


public class GAMainboard implements IMainboard{

    /**
     * 主板卡槽
     */
    private int  cpuHoles;

    public GAMainboard(int cpuHoles) {
        this.cpuHoles = cpuHoles;
    }

    public void installCPU() {
        System.out.println("GAMainboard 安装Cpu, 主板卡槽个数:" + this.cpuHoles);
    }
}

/**
 * 微星主板
 */
public class MSIMainboard implements IMainboard{

    /**
     * 主板卡槽
     */
    private int  cpuHoles;

    public MSIMainboard(int cpuHoles) {
        this.cpuHoles = cpuHoles;
    }

    public void installCPU() {
        System.out.println("MSIMainboard 安装Cpu, 主板卡槽个数:" + this.cpuHoles);
    }
}

3 采用简单工厂模式,需要两个工厂

public class CPUFactory {
    /**
     * 根据type创建cpu
     * @param type 0 Intel 1 Amd
     * @return cpu具体实例
     */
    public static ICPU createCpu(int type) {
        if (type == 0) {
            return new IntelCPU(1099);
        } else {
            return new AmdCPU(899);
        }
    }
}


public class MainboardFactory {

    /**
     * 根据type创建主板
     * @param type 0 GA 1 MSI
     * @return 主板具体实例
     */
    public static IMainboard createMainboard(int type) {
        if (type == 0) {
            return new GAMainboard(1099);
        } else {
            return new MSIMainboard(899);
        }
    }
}

4 装机工程师

/**
 * 组装电脑
 */
public class ComputerEnginer {

    private ICPU cpu;

    private IMainboard mainboard;

    public void assembleComputer(int cpuType, int mainboardType) {
        // 准备cpu 和 主板
        prepareHardwares(cpuType, mainboardType);

        // 组装完成
    }

    /**
     * 准备cpu 和 主板
     *
     * @param cpuType       cpu 类型
     * @param mainboardType 主板类型
     */
    private void prepareHardwares(int cpuType, int mainboardType) {
        cpu = CPUFactory.createCpu(cpuType);
        mainboard = MainboardFactory.createMainboard(mainboardType);

        // 测试
        cpu.calcuator();
        mainboard.installCPU();
    }
}

5 客户端使用

public class Client {
    public static void main(String[] args) {
        ComputerEnginer computerEnginer = new ComputerEnginer();
        computerEnginer.makeComputer(1,0);
    }
}

结果

有何问题 ? 

可以看到结果中 Cpu 和 主板卡槽的个数并不匹配。装机电脑其实是有问题的。所以客户端在选择组件的时候就需要考虑到 cpu 和主板之间的以来关系,才能正确的创造出电脑。但是客户端可能并不知道具体的匹配关系。那怎么解决这个问题呢 ?

这个时候我们可以提供几套选择的套餐空客户端选择。套餐里面已经定义好了cpu 和 主板之间的关系。对应的解决方案就是抽象工厂模式。 

4.2 抽象工厂模式概念

定义:提供一个创建一系列相关或者相互依赖对象的接口,而无需指定他们具体实现类。

先来看下UML类图

 

AbstractFactory: 抽象工厂,里面定义了创建产品的抽象方法。

ConcreteFactory1/2: 具体的工厂,相当于前面所说的套餐。

IProductA/B: 产品接口

IProductA/B(1,2): 产品具体实现类。 产品A 和 产品B 之间有相应的对应关系。需要选对匹配。

4.3 来看下具体的实例

基于前面提高的 cpu 和 主板来进行实例。 cpu 和 主板的 接口和实现前面有,这里不在冗余创建。 主要是将工厂类进行改造,两面所创建按的两个静态工厂就不需要了。

1 AbstractFactory 和 实现类

/**
 * 抽象工厂模式。抽象工厂
 */
public interface AbstractFactory {

    public ICPU createCPU();

    public IMainboard createMainboard();

}

/**
 * 装机方案一
 */
public class Schem1 implements AbstractFactory {
    public ICPU createCPU() {
        return new IntelCPU(1099);
    }
    public IMainboard createMainboard() {
        return new GAMainboard(1099);
    }
}


/**
 * 装机方案二
 */
public class Schem2 implements AbstractFactory {
    public ICPU createCPU() {
        return new AmdCPU(899);
    }
    public IMainboard createMainboard() {
        return new MSIMainboard(899);
    }
}
2 装机工程师
/**
 * 组装电脑
 */
public class ComputerEnginer {

    private ICPU cpu;

    private IMainboard mainboard;

    public void makeComputer(AbstractFactory scheme) {
        // 准备cpu 和 主板
        prepareHardwares(scheme);
        // 组装完成
    }

    /**
     * 准备cpu 和 主板
     *
     * @param scheme 装机方案
     */
    private void prepareHardwares(AbstractFactory scheme) {
        cpu = scheme.createCPU();
        mainboard = scheme.createMainboard();
        // 测试
        cpu.calcuator();
        mainboard.installCPU();
    }
}

这里在创建cpu  和 主板的时候,直接使用 AbstractFactory  具体实现类,方案 scheme进行创建就可以了,保证了对应关系。

3 客户端使用

public class Client {

    public static void main(String[] args) {
        AbstractFactory schem1 = new Schem1(); // 方案一
        AbstractFactory schem2 = new Schem2(); // 方案二

        ComputerEnginer computerEnginer = new ComputerEnginer();
        computerEnginer.makeComputer(schem1);
        System.out.println("--------------------------切换装机方案-----------------");
        computerEnginer.makeComputer(schem2);
    }
}

客户端选择的时候,只需要选择具体的方案就可以了,不需要单独选择 cpu 或 主板。不需要知道它们之间的对应关系。

结果:

 

4.4 抽象工厂模式的优点和缺点

优点:

  • 分离了接口和实现
  • 维护了系列产品之间的依赖关系。使得产品切换容易。

缺点:

  • 不太容易扩展产品,比如组装笔记本还需要配件内存。那么 AbstractFactry 和 具体的工厂类,还有装机工程师类都需要做更改。

 

五、 三种工厂模式之间的关系

工厂方法模式, 如果就在具体的工厂父类里面创建实例就退化为 简单工厂模式。

抽象工厂模式,如果产品族中只有一个产品,那么就退化为工厂方法模式。

简单工厂模式 ---> 工厂方法模式---->抽象工厂模式。 解决的问题越来越复杂。

在实际的运用中,根据具体的业务场景选择合适的即可。

 

参考资料

 

书籍:<<研磨设计模式>>  陈臣,王斌 著

博客:https://blog.csdn.net/varyall/article/details/82355964

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值