研磨23种大话设计模式------简单工厂模式 + 工厂方法模式 + 抽象工厂模式

大家好,我是一位在java学习圈中不愿意透露姓名并苟且偷生的小学员,如果文章有错误之处,还望海涵,欢迎多多指正

如果你从本文 get 到有用的干货知识,请帮忙点个赞呗,据说点赞的都拿到了offer

简单工厂模式

简介:

  1. 不算是23种中的一个
  2. 在开发中非常的常用
  3. 可以当做学习23种设计模式之前的一个热身来学习

引申案例

有一天我走在路上,发现路边有两家烤肉店,就在我打算不再看第二眼时,也不知是哪家烤肉店,突然传来了烤肉的香味,馋的我那是口水直流,于是我先去第一家烤肉店吃了一遍,然后又跑去第二家又吃了一遍,然后带着我沉重的肚几满载而归…

案例分析

通过案例不难发现,案例中出现了两家烤肉店和我,而关系是烤肉店卖烤肉给我(只关注这一种关系),于是可以写出案例代码如下(包含了简单工厂实现的代码注意区分,后面在细说):

第一家烤肉店
package factory.simplefactory;

/**
 * @author xingyu
 * @date 2021/9/4 18:55
 * 烤肉店 1
 * 功能:卖烤肉
 */
public class BarbecueShopOne implements AbstractBarbecueShop {
    @Override
    public void saleBarbecue() {
        System.out.println("烤肉 1");
    }

	/**
     * 一开始自己卖自己的
     */
    public void saleBarbecueOne() {
        System.out.println("烤肉 1");
    }
}

第二家烤肉店
package factory.simplefactory;

/**
 * @author xingyu
 * @date 2021/9/4 18:55
 * 烤肉店 2
 * 功能:卖烤肉
 */
public class BarbecueShopTwo implements AbstractBarbecueShop {

    @Override
    public void saleBarbecue() {
        System.out.println("烤肉 2");
    }

	/**
     * 一开始自己卖自己的
     */
    public void saleBarbecueTwo() {
        System.out.println("烤肉 2");
    }
}

我(此处我是用户,即是主方法)
package factory.simplefactory;

/**
 * @author xingyu
 * @date 2021/9/4 18:59
 */
public class Test {

    public static void main(String[] args) {
        //一开始自己卖自己的,自己写自己的方法名不好记不方便管理
        //也就是两家烤肉店既然都是卖烤肉的,何不统一一个规则去卖
        BarbecueShopOne one = new BarbecueShopOne();
        BarbecueShopTwo two = new BarbecueShopTwo();
        one.saleBarbecueOne();
        two.saleBarbecueTwo();
    }
}

两家烤肉店既然都是卖烤肉的,何不统一一个规则去卖?
因为如果第一家烤肉店和第二家烤肉店卖烤肉的方法名如果不一致,而我作为用户(客户)去两家店买烤肉时需要记住两个方法的名字,不方便使用,因此出现了烤肉店卖烤肉的规则

烤肉店卖烤肉的规则
package factory.simplefactory;

/**
 * @author xingyu
 * @date 2021/9/4 18:57
 * 每个店都要卖烤肉,那就遵循一个规则
 */
public interface AbstractBarbecueShop {

    /**
     * 卖烤肉的规则
     */
    void saleBarbecue();
}

不难发现多了规则以后,我作为用户在主方法中就可以这么写了:

		//烤肉店实现规则,统一卖烤肉的规则,有了一定的秩序
		//作为用户我只要知道这一种卖烤肉的方法名即可
        AbstractBarbecueShop one = new BarbecueShopOne();
        AbstractBarbecueShop two = new BarbecueShopTwo();
        one.saleBarbecue();
        two.saleBarbecue();
分析此时的案例代码

缺点:

  1. 按照设计原则,违背了一个封装隔离的思想,我作为用户知道了类的具体实现,知道人家是怎么卖的(为什么说是缺点,其实生活中也可以举出例子,你去理发店理发,你并不知道谁理的好,你要是知道直接让那个厉害的师傅给你理发就好了,其他理发师不就没活干了嘛)
  2. 烤肉店对象不方便管理,一旦改动new的店变了,假设之前new了很多行,修改起来也麻烦(假设之前new BarbecueShopOne()执行了10次,此时我换个店,那么就需要修改10行代码)

优化思路:烤肉店对象的创建交给工厂来实现,这样一来既隐藏了类的具体实现,也方便对象的统一管理,代码如下:

简单工厂
package factory.simplefactory;

/**
 * @author xingyu
 * @date 2021/9/4 18:53
 * 工厂创建烤肉店实例,而不是使用者来创建,ioc控制反转
 */
public class BarbecueFactory {

	/**
     * 既隐藏了类的具体实现,也方便对象的统一管理
     * 但不灵活,只能固定一种对象,一家烤肉店
     */
    public static AbstractBarbecueShop getBarbecueShop(){
        return new BarbecueShopOne();
    }
	
    /**
     * 只能实现对 对象 创建方式的统一管理
     * 虽然变灵活了,但也同样暴露了具体类(因为用户需要知道参数的含义)
     * 加个static也称为静态工厂
     * 知道具体要创建那个类对象的情况下这样写
     */
    public static AbstractBarbecueShop getBarbecueShop(Class clazz){
        AbstractBarbecueShop shop = null;
        try {
            shop = (AbstractBarbecueShop) clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return shop;
    }
}

此时主方法中可以这么写:

		//暴露类的具体实现的写法,但灵活
        AbstractBarbecueShop one = BarbecueFactory.getBarbecueShop(BarbecueShopOne.class);
        AbstractBarbecueShop two = BarbecueFactory.getBarbecueShop(BarbecueShopTwo.class);
        //隐藏类具体实现的写法,不灵活
        AbstractBarbecueShop three = BarbecueFactory.getBarbecueShop();
        one.saleBarbecue();
        two.saleBarbecue();

到此为止简单工厂模式的应用结束,是不是感觉很简单呢!

简单工厂模式总结

缺点:创建的对象单一,不灵活
应用场景:隐藏类的具体实现,统一管理类对象的创建方式

工厂方法模式

简介

其实工厂方法模式就是在简单工厂模式的基础上实现了既能隐藏类的具体实现,又能统一管理类对象的创建的功能,本质来看:简单工厂是工厂方法的一个特例,为什么这么说等理解了接下来的工厂方法就知道了

引申案例

接着简单工厂模式的案例,我们知道简单工厂对于创建的对象并不灵活,只能创建单一的对象(代码写死了),而想要灵活又会暴露类的具体实现,因此工厂方法模式在此基础上进行了改进

案例分析

可以将一个工厂看做一个方法(这种情况下),我还是找原来的那个工厂做事,但原来的那个工厂不自己做事了,交给子工厂来做事,不同的子工厂做不同的事,也就是创建不同的对象(原来的那个工厂此时叫总工厂,有点无为而治感觉),这里也就是我说的一个工厂对应一个方法,创建一种对象

案例代码

总工厂(自己不做事)
package factory.factorymethod;


/**
 * @author xingyu
 * @date 2021/9/4 18:53
 * 工厂创建烤肉店实例,ioc控制反转
 */
public abstract class BarbecueFactory {

    /**
     * 交给子工厂实现具体的创建哪个对象的方法
     * @return
     */
    protected abstract AbstractBarbecueShop sonFactoryCreateObj();

	/**
     * 提供给对外访问的方法,隐藏具体做事方法的方法名
     */
    public AbstractBarbecueShop getBarbecueShop(){
        return sonFactoryCreateObj();
    }
}

子工厂一号
package factory.factorymethod;

/**
 * @author xingyu
 * @date 2021/9/4 21:47
 */
public class BarbecueFactoryOne extends BarbecueFactory {

    /**
     * 专门用来创建BarbecueShopOne实例的工厂的方法
     * @return
     */
    @Override
    protected AbstractBarbecueShop sonFactoryCreateObj() {
        return new BarbecueShopOne();
    }
}

子工厂二号
package factory.factorymethod;

/**
 * @author xingyu
 * @date 2021/9/4 21:50
 */
public class BarbecueFactoryTwo extends BarbecueFactory {
    @Override
    protected AbstractBarbecueShop sonFactoryCreateObj() {
        return new BarbecueShopTwo();
    }
}

这样一来,我作为用户在主方法中:

package factory.factorymethod;

/**
 * @author xingyu
 * @date 2021/9/4 18:59
 */
@SuppressWarnings("all")
public class Test {

    public static void main(String[] args) {
        /**
         * 实际上可以将简单工厂看做是工厂方法模式的特例
         * 我们获取对象不是直接new,而是通过别人帮我们获取对象
         * 此时的工厂我们要么只能取一种对象,要么用户传参告知要什么对象,然后由工厂帮我们创建
         * 但用户告知需要参数,势必要知道参数的含义,一样要暴露具体类的实现
         * 因此我们创建一个总工厂(工厂),工厂下一个抽象方法创建用户想要的对象
         * 用户想要的对象总工厂并不知道,但用户需要知道一个对象需要一个对应的工厂来创建
         * 而这个工厂就是总工厂的子类,也就是子工厂,它知道用户要的是什么对象
         * 因此总工厂不做事(无为而治),只是提供给外界一个接口调用方法去具体子工厂中创建用户需要的对象
         * 工厂方法模式相比于简单工厂扩展性更好,以后创建新的对象只需要写个工厂继承工厂,子工厂直接创建即可
         * 可以理解为对象被包装起来了,用户只要知道怎么用就行,而不需要关注对象是怎么创建的
         * 工厂方法和简单工厂的应用场景:创建对象时隐藏对象的真实类型,统一管理对象的创建方式
         */
        BarbecueFactory factoryOne = new BarbecueFactoryOne();
        BarbecueFactory factoryTwo = new BarbecueFactoryTwo();
        AbstractBarbecueShop one = factoryOne.getBarbecueShop();
        AbstractBarbecueShop two = factoryTwo.getBarbecueShop();
        one.saleBarbecue();
        two.saleBarbecue();

    }
}

此时虽然看起来很浪费,一种对象的创建就需要写个子工厂来实现,但从用户的角度而言,用户的关注点就变为只要知道对象怎么用就好了,对象的创建一条龙服务包给工厂来做了

工厂方法模式总结

相比于简单工厂优点:创建的对象多样化,更灵活,可扩展性更好
应用场景:隐藏类的具体实现,统一管理类对象的创建方式

抽象工厂模式

简介

抽象工厂模式是在工厂方法模式的基础上实现对多个对象创建以及对象之间联系的管理
关注的点在于对象与对象之间的联系

引申案例

我去电脑城买一套电脑(假设只需要主板和cpu)

  • 首先我找到一家卖电脑的店,在店中挑选了我想要的组件,一块主板,一个cpu
  • 然后店里的工程师帮我去组装成一个完整的电脑

案例分析

案例中出现的对象:电脑工程师,我(用户/主方法),一块主板,一个cpu
对象之间的关系:主板需要和cpu进行组装;cpu能进行核心计算;工程师负责组装主板和cpu;我负责挑选主板和cpu…(只关注这些)

案例代码

主板规则

只要是主板就要有名字,引脚数等等属性

package factory.abstractfactory.nopattern;

/**
 * @author xingyu
 * @date 2021/9/5 10:38
 * 主板的规则
 */
public abstract class MainBoard {

    //cpu名称
    protected String name;
    //引脚数
    protected int pin;

    /**
     * 只要是主板就能够组装cpu
     */
    abstract void assembleCpu();
}

具体的主板one
package factory.abstractfactory.nopattern;

/**
 * @author xingyu
 * @date 2021/9/5 10:28
 */
public class MainBoardOne extends MainBoard {

    public MainBoardOne(String name, int pin) {
        this.name = name;
        this.pin = pin;
    }

    @Override
    public void assembleCpu() {
        System.out.println(name + "和cpu的组装,此主板引脚数为:" + pin);
    }
}

具体的主板two
package factory.abstractfactory.nopattern;

/**
 * @author xingyu
 * @date 2021/9/5 10:30
 */
public class MainBoardTwo extends MainBoard {

    public MainBoardTwo(String name, int pin) {
        this.name = name;
        this.pin = pin;
    }


    @Override
    public void assembleCpu() {
        System.out.println(name + "和cpu的组装,此主板引脚数为:" + pin);
    }

}

Cpu的规则

只要是cpu就有名字和针脚数等等…

package factory.abstractfactory.nopattern;
package factory.abstractfactory.nopattern;

/**
 * @author xingyu
 * @date 2021/9/5 10:35
 * Cpu的规则
 */
public abstract class Cpu {

    //cpu名称
    protected String name;
    //针脚数
    protected int stitch;

    /**
     * 只要是cpu,就必须要有cpu的核心计算方法
     */
    abstract void coreCalculate();
}

具体的cpu one
package factory.abstractfactory.nopattern;

/**
 * @author xingyu
 * @date 2021/9/5 10:30
 */
public class CpuOne extends Cpu {

    public CpuOne(String name, int stitch) {
        this.name = name;
        this.stitch = stitch;
    }

    @Override
    public void coreCalculate() {
        System.out.println(name + "的核心计算,此cpu针脚数为:" + stitch);
    }
}

具体的cpu two
package factory.abstractfactory.nopattern;

/**
 * @author xingyu
 * @date 2021/9/5 10:30
 */
public class CpuTwo extends Cpu {


    public CpuTwo(String name, int stitch) {
        this.name = name;
        this.stitch = stitch;
    }

    @Override
    public void coreCalculate() {
        System.out.println(name + "的核心计算,此cpu针脚数为:" + stitch);
    }
}

电脑工程师

负责组装用户挑选的组件

package factory.abstractfactory.nopattern;

import factory.abstractfactory.withpattern.AbstractFactory;

/**
 * @author xingyu
 * @date 2021/9/5 10:34
 * 电脑工程师
 */
public class ComputerEngineer {
    /**
     * 用户告知工程师自己想要哪种主板和哪种cpu
     * @param mainboardType
     * @param cpuType
     */
    public void assembleComputer(int mainboardType, int cpuType){
        MainBoard mainBoard = null;
        Cpu cpu = null;
        if(mainboardType == 1){
            mainBoard = new MainBoardOne("mainboardOne", 1175);
        } else if(mainboardType == 2){
            mainBoard = new MainBoardTwo("mainboardTwo", 775);
        } else {
            //多了在加,实际上可以用简单工厂实现就不用if,else了
        }
        if(cpuType == 1){
            cpu = new CpuOne("cpuOne", 1175);
        } else if(cpuType == 2){
            cpu = new CpuTwo("cpuTwo", 775);
        } else {
            //多了在加,实际上可以用简单工厂实现就不用if,else了
        }
        //工程师检查用户选的是否合理(引脚数和针脚数必须一致才能组装)
        checkComponent(mainBoard, cpu);
        //检查完毕正确组装,各项指标正常
        mainBoard.assembleCpu();
        cpu.coreCalculate();
    }
    private void checkComponent(MainBoard mainBoard, Cpu cpu) {
        if(mainBoard.pin != cpu.stitch){
            throw new IllegalArgumentException(mainBoard.name + "与" + cpu.name + "不适配");
        }
    }
}

我作为用户,主方法如下:
package factory.abstractfactory;

import factory.abstractfactory.nopattern.ComputerEngineer;
import factory.abstractfactory.withpattern.AbstractFactory;
import factory.abstractfactory.withpattern.PlanA;

/**
 * @author xingyu
 * @date 2021/9/4 22:31
 */
@SuppressWarnings("all")
public class Test {

    public static void main(String[] args) {
        ComputerEngineer engineer = new ComputerEngineer();
        //用户想要类型 1 的主板和类型 1 的cpu
        engineer.assembleComputer(1, 1);
        //用户想要类型 1 的主板和类型 2 的cpu
        engineer.assembleComputer(1, 2);
    }
}

执行结果如下:

在这里插入图片描述
不难发现,如果用户挑选的主板和cpu不适配,那么工程师就会组装不成功,因此店老板改为出售套餐,将适配的主板和cpu放在一起出售,然后由工程师组装即可,这也就是抽象工厂,因为此时主板与cpu已经有了某种联系(需要适配),此时由一个抽象工厂提供两个方法创建适配的主板和cpu对象,同理由于需要灵活,不能写死就一种套餐,因此可以用子工厂来具体实现,不难发现抽象工厂是在工厂方法的基础上进行改进的

抽象工厂实现的代码

总工厂
package factory.abstractfactory.withpattern;

import factory.abstractfactory.nopattern.Cpu;
import factory.abstractfactory.nopattern.MainBoard;

/**
 * @author xingyu
 * @date 2021/9/5 11:29
 * 抽象工厂
 * 作用:提供套餐供用户选择,也就是创建一个具体的主板对象,和一个具体的cpu对象
 * 同时这两个对象之间是存在联系的(此处的联系就是主板引脚数 == cpu针脚数)
 */
public interface AbstractFactory {

    MainBoard createMainBoard();

    Cpu createCpu();

}

子工厂one也就是一种套餐
package factory.abstractfactory.withpattern;

import factory.abstractfactory.nopattern.Cpu;
import factory.abstractfactory.nopattern.CpuOne;
import factory.abstractfactory.nopattern.MainBoard;
import factory.abstractfactory.nopattern.MainBoardOne;

/**
 * @author xingyu
 * @date 2021/9/5 11:30
 * 抽象工厂的具体实现
 * 具体需要创建哪个主板和cpu
 * PlanA 创建的是MainBoardOne和CpuOne
 */
public class PlanA implements AbstractFactory {
    @Override
    public MainBoard createMainBoard() {
        return new MainBoardOne("MainBoardOne", 1175);
    }

    @Override
    public Cpu createCpu() {
        return new CpuOne("CpuOne", 1175);
    }
}

子工厂two也就是一种套餐
package factory.abstractfactory.withpattern;

import factory.abstractfactory.nopattern.Cpu;
import factory.abstractfactory.nopattern.CpuTwo;
import factory.abstractfactory.nopattern.MainBoard;
import factory.abstractfactory.nopattern.MainBoardTwo;

/**
 * @author xingyu
 * @date 2021/9/5 11:30
 * 抽象工厂的具体实现
 * PlanB 创建的是MainBoardTwo和CpuTwo
 */
public class PlanB implements AbstractFactory {
    @Override
    public MainBoard createMainBoard() {
        return new MainBoardTwo("MainBoardTwo", 775);
    }

    @Override
    public Cpu createCpu() {
        return new CpuTwo("CpuTwo", 775);
    }
}

主方法:
		/**
         * 分析发现,让用户自己挑选可能会导致主板与cpu不适配
         * 因此店家改成套餐售卖(即适配的主板和cpu在一块组成一个套餐)
         * 用户只需要挑选想要的套餐即可
         * 由此引申出抽象工厂,用户需要一个套餐,套餐有多种
         *  简单工厂,工厂方法创建的都是一个对象,而抽象工厂创建的是一个方案/体系/套装
         *  而当这个体系只有一个对象时也可以将抽象工厂看做工厂方法
         *  抽象工厂模式 更注重一系列对象互相之间的依赖关系
         *  抽象工厂模式的方法不是随意搭建起来的 一系列或者是相互依赖的方法
         */
        //用户挑选想要的套餐
        AbstractFactory planA = new PlanA();
        //将自己挑选的套餐告知工程师,然后组装电脑
        ComputerEngineer engineer = new ComputerEngineer();
        engineer.assembleComputer(planA);

简单工厂,工厂方法,抽象工厂总结:

  • 创建单个对象:简单工厂,工厂方法

  • 创建多个对象,且对象之间有着某种联系:抽象工厂

  • 用于隐藏类的真实实现,统一管理类对象的创建方式:简单工厂,工厂方法,抽象工厂

看完了的小伙伴们是不是对三大工厂模式有了更深刻的理解呢?希望这篇文章能帮到小伙伴们更好的理解三大工厂的设计思想

之后开始慢慢的更新每一种设计模式,通过生动形象的生活现象举例带你感受设计模式的世界,其实设计模式不难,只是当我们面对某个场景时想不到用哪个设计模式该不该用设计模式,怎样用才更合理…
博客内容来自腾讯课堂渡一教育拓哥,以及自己的一些理解认识,同时看了其他大牛写的设计模式技术文章综合总结出来的

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值