Java设计模式——4.工厂模式和抽象工厂模式

4.工厂模式和抽象工厂模式

4.1概述

在Java的世界中,万物皆对象,我们通常使用new关键字来创建对象。现实中,这里用小米生态链举例,小米生态链有众多产品,如手机、笔记本、电视、空调、洗衣机、手表、路由器等,每一个产品种类下还有多种型号。当然这些产品也肯定不会在同一个工厂里生产,一个工厂没办法管理这么多不同类的产品呀!所以一般会分成不同的工厂——大家电工厂、手机工厂、笔记本工厂等。但是,这些都不是我们消费者所关心的事,我们只需要在小米商城里下单,然后由商城根据订单帮我们找相应工厂去生产产品即可。对消费者而言,工厂只是一个抽象的概念,只知道工厂能够生产出他想购买的产品。

4.2简单工厂模式(Simple Factory)

假设这样一个场景,有一个IProduct的产品接口,下面有5个实现类Product1、Product2、Product3、Product4、Product5。他们属于一个大类,可以通过一个工厂去管理他们的生成。但这五个商品的类型还是略有不同,所以初始化有所不同。为了方便使用产品工厂(ProductFactory)类来创建这些产品的对象,用户可以通过产品号来确定需要哪种产品,这个过程如下图所示。

这样,这个工厂可以根据订单中的产品编号来生产对应的商品,伪代码如下:

public class ProductFactory{
    public static IProduct createProduct(String productNo){
    
        switch(productNo){
            case "1": return new Product1();
            case "2": return new Product2();
            case "3": return new Product3();
            case "4": return new Product4();
            case "5": return new Product5();
            default: throw new NotSupportedException("暂时不支持生产此编号商品!");
        }
    }
}

对于程序调用者来说,他只需要知道通过工厂的createProduct方法,指定商品编号productNo就可以得到对应的商品了,而商品也满足接口IProduct的规范,所以初始化就简单了。此时要创建产品对象就要把新商品的规则写入工厂类中。

简单工厂模式的缺点也很明显了:简单工厂模式违反对扩展开放,对修改关闭的“开闭原则”,如果要新增一个商品,就必须修改工厂类的代码,耦合性太高,不利于维护。经过改进后,工厂方法模式出现了。

工厂方法模式:

它定义一个用来创建对象的接口,让子类决定实例化哪一个类,让子类决定实例化延迟到子类。工厂方法模式是针对每个产品提供一个工厂类,在客户端中判断使用哪个工厂类去创建对象。

简单工厂和工厂方法模式的比较:

对于简单工厂模式而言,创建对象的逻辑判断放在了工厂类中,客户不感知具体的类,但是其违背了开闭原则,如果要增加新的具体类,就必须修改工厂类;对于工厂方法模式而言,是通过扩展来新增具体类的,符合开闭原则,但是在客户端就必须要感知到具体的工厂类,也就是将判断逻辑由简单工厂的工厂类移动到客户端。

 4.3抽象工厂模式(AbstractFactory)

它提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂模式可以向客户端提供一个接口,使得客户端在不必指定产品的具体情况下,创建多个产品族中的产品对象。

对于普通工厂而言,它解决了一类对象创建问题,但是有时候对象很复杂,有十几种,又分为好几个类别。如果只有一个工厂,面对如此多的商品,这个工厂需要实现的逻辑就太复杂了,所以我们希望把工厂细分一下,这样有利于工厂产品规则的维护。同时,设计者并不想让调用者知道具体的工厂规则,只希望让他们知道有一个统一的工厂就行了。这样进行设计,有助于对外封装的和简化调用者的使用,因为调用者也不想知道选择具体工厂的规则!示意图如下:

客户只是认为有一个能够生产各类产品的工厂,能够生产自己所需的产品,这里的工厂只是一个虚拟的概念,并不真实存在,它是通过车厂内部的各个分厂实现的,这个虚拟工厂就称为抽象工厂,它的各个分厂称为具体工厂。

在设计中要保证工厂接口统一,便于规范管理,所以这里让所有具体工厂和抽象工厂都实现IProductFactory接口。

public interface IProductFactory {
    public Product createProduct(String productNo);
}

我们这里有三个工厂:PhoneFactory、TVFactory、PCFactory,但是我们不需要把这三个工厂提供给调用者,调用者不需知道这里面的逻辑。所以使用一个ProductFactory抽象工厂来做为直接和调用者连接的公共工厂,由他内部的逻辑选择具体的工厂。为了简单理解,我们做如下工厂选择规则。

  • 产品编号以1开头的订单送往手机工厂。
  • 产品编号以2开头的订单送往电视工厂。
  • 产品编号以3开头的订单送往电脑工厂。
  • 每个工厂内部只有一个商品,不展开里面的逻辑。

我们下面模拟了三个具体工厂和一个抽象工厂。

/************************具体工厂*********************************/

public class PhoneFactory implements IProductFactory {
    public Product product=null;
    @Override
    public Product createProduct(String productNo) {
        product=new Product(productNo,"小米手机10");
        return product;
    }

}

public class TVFactory implements IProductFactory {
    public Product product=null;
    @Override
    public Product createProduct(String productNo) {
        product=new Product(productNo,"小米电视4C");
        return product;
    }

}

public class PCFactory implements IProductFactory {

    private Product product=null;
    @Override
    public Product createProduct(String productNo) {
        product=new Product(productNo,"小米笔记本2020");
        return product;
    }
}

/************************抽象工厂*********************************/

public class ProductFactory implements IProductFactory {
    @Override
    public Product createProduct(String productNo) {

        char ch=productNo.charAt(0);
        IProductFactory factory=null;
        switch (ch){
            //No开头为1,手机工厂;为2,电视工厂;为3,电脑工厂
            case '1':
                factory=new PhoneFactory();
                break;
            case '2':
                factory=new TVFactory();
                break;
            case '3':
                factory=new PCFactory();
        }
        if(factory!=null){
            Product product=factory.createProduct(productNo);
            System.out.println("我是【"+ch +"】号工厂,我生产了编号为
                【"+product.getProductNo()+"】的【"+product.getName()+"】产品!");
            return product;
        }
        return null;
    }
}

这里简化Product的处理,直接用一个类来模拟。

public class Product {
    private String productNo;
    private String name;

    public Product(){}
    public Product(String productNo, String name) {
        this.productNo = productNo;
        this.name = name;
    }

    public String getProductNo() {
        return productNo;
    }

    public String getName() {
        return name;
    }
}

通过new一个抽象工厂实例,调用createProduct方法,传入产品编号参数,这样抽象工厂将按代码逻辑指定具体工厂去生产产品。

测试代码如下:

    @Test
    public void buy(){
        ProductFactory factory=new ProductFactory();

        factory.createProduct("1000");
        factory.createProduct("3000");

    }

运行结果如下:

 

总结:

通过抽象工厂可以知道,只需要提供产品编号productNo,就能通过ProductFactory创建产品的对象了。这样调用者不需要去理会ProductFactory选择了哪个具体工厂的规则对于设计者而言,ProductFactory就是一个抽象工厂,这样创建对象对调用者而言就简单了。每个工厂也只需要维护它所属类型产品对象的生产,具体的工厂规则也不会难以维护。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值