设计模式详解(二):抽象工厂——Abstract Factory

抽象工厂及其作用

工厂方法是一种创建型设计模式。所谓创建型设计模式是说针对创建对象方面的设计模式。在面向对象的编程语言里,我们通过对象间的相互协作,共同完成复杂的业务逻辑,因而对象之间存在着依赖关系。
在上一篇文章设计模式详解(一):工厂方法——Factory Method中,我们知道,工厂方法是通过一个方法扮演对象工厂的角色,为依赖方提供对象,避免直接调用所依赖类的构造器,从而实现解耦的目的。
在有些业务场景下,我们依赖于一组对象,这些对象就像一套零件一样,当变换实现时,往往需要成套换掉。
比如,我们模仿家具城售卖家具的场景,家具城可对外出售桌子、椅子,沙发三件套家具,用户可定制风格,如艺术、现在等。

public class FurnitureShop {
    //... some business logic
    public void sell(String style) {
        Chair chair = provideChair(style);
        Sofa sofa = provideSofa(style);
        Table table = provideTable(style);
        System.out.println("sell chair:" + chair + ",sofa:" + sofa + ",table:" + table);
    }

    private Chair provideChair(String style) {
        switch (style) {
            case "艺术":
                return new ArtChair();
            case "现代":
                return new ModernChair();
            //...
        }
        return new DefaultChair();
    }

    private Sofa provideSofa(String style) {
        switch (style) {
            case "艺术":
                return new ArtSofa();
            case "现代":
                return new ModernSofa();
            //...
        }
        return new DefaultSofa();
    }

    private Table provideTable(String style) {
        switch (style) {
            case "艺术":
                return new ArtTable();
            case "现代":
                return new ModernTable();
            //...
        }
        return new DefaultTable();
    }

    //... some business logic
}

观察上面的代码发现,Chair、Sofa、Table三种对象总是按照某种风格成套提供,当需要切换风格时,需要同步修改provideChair、provideSofa、provideTable三处,很不方便。另外,FurnitureShop类的主要职责时售卖家具,而不是创建相关的对象,但现在的实现里充斥大量负责创建对象的工厂方法。
抽象工厂设计模式是将一组互有联系,成套提供对象的工厂方法从依赖方抽离出来,放到一个单独的类中,这个类充当一个总管的角色,管理这些工厂方法。
上面的代码用抽象工厂的设计理念实现如下:

//定义家具套FurnitureSuit接口,担任抽象工厂的角色,提供同种风格的一套家具。
public interface FurnitureSuit {
    Chair provideChair();

    Sofa provideSofa();

    Table provideTable();
}

根据需要实现不同的抽象工程类,如艺术风格

public class ArtFurnitureSuit implements FurnitureSuit {
    @Override
    public Chair provideChair() {
        return new ArtChair();
    }

    @Override
    public Sofa provideSofa() {
        return new ArtSofa();
    }

    @Override
    public Table provideTable() {
        return new ArtTable();
    }
}

最后是依赖方的代码,

public class FurnitureShop {
    //... some business logic
    public void sell(String style) {
        FurnitureSuit suit = provideFurnitureSuit(style);
        Chair chair = suit.provideChair();
        Sofa sofa = suit.provideSofa();
        Table table = suit.provideTable();
        System.out.println("sell chair:" + chair + ",sofa:" + sofa + ",table:" + table);
    }

    private FurnitureSuit provideFurnitureSuit(String style) {
        switch (style) {
            case "艺术":
                return new ArtFurnitureSuit();
            case "现代":
                return new ModernFurnitureSuit();
            //...
        }
        return new DefaultFurnitureSuit();
    }
    //... some business logic
}

抽象工厂的好处

  • 解耦。同工厂方法一样,抽象工厂避免对象之间实现类的强耦合,另外便于逻辑上成套提供服务的一组对象的切换,出自同一抽象工厂的对象之间必然是互相兼容。
  • 符合单一职责的原则。类的设计原则之一便是单一职责。依赖方的主要业务功能并不是创建所依赖的对象,将这一组对象的创建任务交给抽象工厂。
  • 符合开闭原则。后续引入更多的抽象工厂的实现类,不会影响到依赖方

抽象工厂的实现

关系图

Abstract Factory为抽象工厂接口定义,ConcreteFactoryX为抽象工厂的不同实现类。依赖方Client只依赖抽象工厂的接口,不依赖抽象工厂的实现类,更不依赖其提供的各种对象的实现类。
在这里插入图片描述

实现步骤

  1. 定义一个抽象工厂接口,该接口定义了一组工厂方法,返回共同服务的各个对象。而方法的返回值也是这些对象对应的接口或基类,而非具体实现类。
  2. 根据需要定义不同特性的抽象工厂类,它们统一实现抽象工厂接口,实现各个方法,返回符合某种特性的各个对象。
  3. 依赖方只依赖抽象工厂接口,当需要不同特性的对象组时,直接更换抽象接口的实现类即可。

抽象工厂的适用场景

  1. 当我们实现业务代码时,无法提前获知所依赖对象的具体类型,这通常发生在顶层设计的时候,此时可以通过工厂方法抽象定义所需的对象。
  2. 我们打包一个库给他人使用时,其中一些业务逻辑,我们无法确定用户是应该使用库里定义好的一些默认组件,还是想使用自己扩展后的组件(继承默认组件),可通过工厂方法解决该问题。
  3. 被依赖的对象可能比较特殊,占用一定的系统资源,所以不能任意创建,可在工厂方法内实现一定的控制逻辑,控制对象的创建过程,避免资源浪费。

抽象工厂举例

在java中,定义了一组与数据库交互的接口,即可JDBC,由于不同数据库厂商的内部原理及运转必然不同,java无从得知,只能定义抽象接口让不同数据库厂商去实现。
Connection接口代码数据库连接,封装了某种数据库连接下的各种功能,实现该接口的数据库需实现并返回与之匹配的SQL语句对象和数据库元数据对象等信息。
不同厂商实现的Connection不同,Connection扮演抽象工厂的角色。

public interface Connection  extends Wrapper, AutoCloseable {
   //...
   Statement createStatement() throws SQLException;
   
   DatabaseMetaData getMetaData() throws SQLException;
   //...
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vincent(朱志强)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值