设计模式学习之工厂设计模式


前言

工厂设计模式属于创建型模式,工厂模式的实现方式可分为简单工厂(静态工厂)、工厂方法、抽象工厂。


一、问题引出

假设有一个用户需要购买手机(apple 和 huawei),使用传统的方式定义手机实体类,用户对象中new具体的手机实体类对象,简单演示如下:

    public void getPhone(String type){
        if ("huawei".equals(type)) {
            new HuaweiPhone();
        }else if("apple".equals(type)){
            new ApplePhone();
        }
    }

这样实现每一个用户都依需要赖于每一个产品类,且每次新增一个新手机类时都需要修改客户代码,违背依赖倒置原则和开闭原则。

类比于生活中,在传统的模式下,用户需要什么对象需要自己创建即自己new 对象,比如需要华为手机和苹果手机,需要自己造出具体的手机产品,这样用户需要了解到生产不同手机的具体参数,需要知道实例创建的具体细节。

二、简单工厂(静态工厂)

引言:随着社会日益发展,工厂角色应运而生,可以理解成手机店,在此种场景下,用户需要手机不需要自己生产了,而是只需要找到手机店(既卖华为手机也卖苹果手机),手机店依据你的需要(参数),提供具体的手机给你。

1.简介

针对上述的问题,首先来看简单工厂模式,简单工厂模式将上述问题中的产品(手机)抽象出来一个父类,提供一个工厂类用来生产产品,用户需要产品时,直接找工厂拿,这样做,用户类不需要依赖于具体的产品类。

简单工厂角色:
 抽象产品角色(Product):是所有产品的父类,负责创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
 具体产品角色(ConcreteProduct):是创建目标,产品的具体类实例。
 工厂角色(Factory):是核心,负责创建所有的产品对象

2.结构图

在这里插入图片描述

3.代码实现

抽象产品父类

public abstract class Phone {
    public void usePhone(){
        System.out.println("使用手机");
    }
}

产品A(华为手机)

public class HuaweiPhone extends Phone {
    @Override
    public void usePhone() {
        super.usePhone();
        System.out.println("华为手机");
    }
}

产品B(苹果手机)

public class ApplePhone extends Phone{
    @Override
    public void usePhone() {
        super.usePhone();
        System.out.println("苹果手机");
    }
}

简单工厂类

public class SimpleFactory {

    public static Phone producePhone(String type){
        if ("huawei".equals(type)) {
            return new HuaweiPhone();
        } else if ("apple".equals(type)) {
            return new ApplePhone();
        }else {
            throw new RuntimeException("not type phone");
        }
    }
}

用户类

public class Client {

    public void buyPhone(String type){
        Phone phone = SimpleFactory.producePhone(type);
        phone.usePhone();
    }

}

测试使用:

   public static void main(String[] args) {
        Client client = new Client();
        client.buyPhone("huawei");
    }


	使用手机
	华为手机

这样使用简单工厂模式改造完成,用户不在依赖于具体的产品实例,实现了解耦。如果需要新增一个产品(小米手机),只需要定义一个具体产品继承Phone类,在工厂创建方法中新增一个分支判断即可,但是这样修改违反了开闭原则,这样做存在的问题:

1、工厂类集中了所有产品创建逻辑,职责过重,一旦发生异常,整个系统将受影响。
2、使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。
3、系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂。
4、简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。

故这种方法只是一种编码方式,并不输入设计模式的一种,且局限于产品种类较少。

三、工厂模式

引言:相比于之前一个手机店卖多个手机的场景,目前市面上出现了华为手机专卖店和苹果手机专卖店,只提供各自专门的产品,假设我们需要手机,一般都说去手机店买手机,手机店相对于具体的华为手机专卖店和苹果手机专卖店来说是一个抽象的存在,可以理解成抽象工厂,而具体的手机店则是具体的工厂,不同的手机店专卖店只能提供对应得手机产品。

1.简介

针对上述的简单工厂实现来看,用户不依赖于具体的手机产品类,但是用户依赖于具体的工厂实现类了。简单工厂是将产品抽象化,具体的产品由工厂去实现,而工厂模式是将工厂也抽样化,使得用户不依赖于具体的工厂。工厂模式提供一个用于创建对象的接口(工厂接口),让其实现类(工厂实现类)决定实例化哪一个类(产品类),并且由该实现类创建对应类的实例。

工厂方法四个角色:
 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的实现方法createProduct() 来创建产品。
 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应

2.结构图

在这里插入图片描述

3.代码实现

抽象产品类,具体产品类同简单工厂中相同

抽象工厂

public interface AbsFactory {
    Phone producePhone();
}

具体工厂(生产华为手机工厂)

public class HuaweiPhoneFactory implements AbsFactory {
    @Override
    public Phone producePhone() {
        return new HuaweiPhone();
    }
}

具体工厂(生产苹果手机工厂)

public class ApplePhoneFactory implements AbsFactory {
    @Override
    public Phone producePhone() {
        return new ApplePhone();
    }
}

客户类

public class Client {

    private final AbsFactory factory;

    public Client(AbsFactory factory){
        this.factory = factory;
    }

    public void buyPhone(){
        Phone phone = factory.producePhone();
        phone.usePhone();
        System.out.println(phone.getClass());
    }
}

测试使用:

public static void main(String[] args) {
        Client client = new Client(new HuaweiPhoneFactory());
        client.buyPhone();
    }


使用手机
华为手机
class com.imooc.demo01.factory.factory.HuaweiPhone

这样实现的好处:
 可以一定程度上解耦,用户和产品实现类隔离开,只依赖产品接口(抽象产品),产品实现类如何改动与用户完全无关。
 可以一定程度增加扩展性,若增加一个产品实现,只需要实现产品接口,修改工厂创建产品的方法,用户可以无感知。
 可以一定程度增加代码的封装性、可读性。清楚的代码结构,对于用户来说很少的代码量,就可以完成很多工作。

4.区别

简单工厂模式与工厂方法模式区别:
 简单工厂模式:本着高内聚低耦合的原则,将系统的逻辑部分和功能分开。
 工厂方法模式:和“简单工厂模式”不同的地方,是加入了“开放-封闭原则”(软件实体类、模块或者函数等等,应该可以扩展,但是不可以修改)规则,将简单工厂的内部判断逻辑,移动到了客户端代码来进行,在扩展新功能的时候,简单工厂模式要修改工厂类,工厂方法模式是只需要修改客户端。

另外,抽象工厂才是实际意义的工厂模式,工厂方法只是抽象工厂的一个比较常见的情况,工厂模式只生产一个产品等级的产品,而抽象工厂可以生产多个产品等级的产品。

四、抽象工厂模式

引言:随着手机的发展,手机和手机的配件比如耳机、充电器分开销售了,买手机不再赠送这些插件,工厂模式中考虑的是手机店只卖手机这一种产品等级的情况,实际的情况中手机店不仅仅出售手机这一种产品,还可能出售耳机、充电器,比如华为专卖店不仅出售华为手机,也出售华为手机充电器及华为耳机。

1.简介

了解抽象工厂模式前,我们先来了解下产品等级和产品族的概念:
  产品等级:不同工厂生产的同一种类型的产品,比如华为手机,苹果手机,小米手机;
  产品族:同一个工厂生产的不是同一种类型的产品,比如华为手机,华为充电器,华为耳机。

在这里插入图片描述
抽象工厂模式是一种为访问类提供一个创建一组相关或相互依赖对象的接口,访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。当系统系统的产品有多于一个的产品族(手机,耳机),而系统只消费其中某一族的产品(华为产品族),使用抽象工厂可以有效的解决了接口选择的问题,具体的解决思路是:在一个产品族里定义多个不同等级的产品(如华为品牌下的手机、耳机),在工厂类里聚合多个同类产品(工厂生产手机和耳机)。

抽象工厂四个角色:
 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法newProduct(),可以创建多个不同等级的产品
 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。

2.结构图

在这里插入图片描述

3.代码实现

抽象产品

/**
 * 手机抽象父类
 */
public abstract class Phone {

    public abstract void usePhone();
}
/**
 * 耳机抽象父类
 */
public abstract class EarPhone {
    public abstract void useEar();
}

具体产品

/**
 * 华为手机
 */
public class HuaweiPhone extends Phone{
    @Override
    public void usePhone() {
        System.out.println("使用华为手机");
    }
}
/**
 * 华为耳机
 */
public class HuaweiEarPhone extends EarPhone{
    @Override
    public void useEar() {
        System.out.println("使用华为耳机");
    }
}
/**
 * 苹果手机
 */
public class ApplePhone extends Phone {
    @Override
    public void usePhone() {
        System.out.println("使用苹果手机");
    }
}
/**
 * 苹果耳机
 */
public class AppleEarPhone extends EarPhone {
    @Override
    public void useEar() {
        System.out.println("使用苹果耳机");
    }
}

抽象工厂

public interface AbsFactory {
    //生产手机
    Phone producePhone();
    //生产耳机
    EarPhone produceEarPhone();
}

具体工厂

/**
 * 华为工厂 生产华为手机和华为耳机
 */
public class HuaweiFactory implements AbsFactory {
    @Override
    public Phone producePhone() {
        return new HuaweiPhone();
    }

    @Override
    public EarPhone produceEarPhone() {
        return new HuaweiEarPhone();
    }
}
/**
 * 苹果工厂 生产苹果手机和苹果耳机
 */
public class AppleFactory implements AbsFactory {
    @Override
    public Phone producePhone() {
        return new ApplePhone();
    }

    @Override
    public EarPhone produceEarPhone() {
        return new AppleEarPhone();
    }
}

用户类

public class Client {
    private final AbsFactory factory;

    public Client(AbsFactory factory){
        this.factory = factory;
    }

    public void buyPhone(){
        Phone phone = factory.producePhone();
        EarPhone earPhone = factory.produceEarPhone();
        phone.usePhone();
        earPhone.useEar();
    }
}

测试使用:

   public static void main(String[] args) {
        Client client = new Client(new HuaweiFactory());
        client.buyPhone();
    }


使用华为手机
使用华为耳机

优点:
  当一个产品族中的多个对象,被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象(华为系列或苹果系列)。
缺点:
  产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。

在这里插入图片描述
在这里插入图片描述
当增加一个新的产品族时只需增加一个新的具体工厂,不需要修改原代码,满足开闭原则。
当产品族中需要增加一个新种类的产品时,则所有的工厂类都需要进行修改,不满足开闭原则。
当系统中只存在一个等级结构的产品时,抽象工厂模式将退化到工厂方法模式。

五、总结

1.使用场景

简单工厂模式:相对于产品种类较少,逻辑判断简单时;
工厂方法模式:客户知道需要创建的工厂,不知道具体的产品,创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
抽象工厂模式:当需要创建的对象是一系列相互关联或相互依赖的产品族时,如华为系列的产品,小米系列产品,系统中有多个产品族,但是每次只使用其中某一族产品。

参考文档:
https://cuizb.top/myblog/article/1655820677
https://baijiahao.baidu.com/s?id=1723461529698148319
https://www.cnblogs.com/tianClassmate/p/16529297.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值