设计模式 - 抽象工厂模式

2020-06-29 15:18:50 浏览数 (1)

预计阅读时间:10-14min

情景引入:

「得不到的永远在骚动,买不起的都是在装聋」

(某商场)

子乾:诶诶诶,这儿有一个 HW 体验店,你确定不进去看看嘛?

朱少:走走瞧瞧看,手机差不多换它一个。

子乾:你看这个,这个音响,我喜欢。

朱少:喜欢有屁用,有钱买吗?我有一个小M的,用着挺好。

子乾:我不,我偏买。我还要手机,俩品牌的都要。

朱少:到家再买。哥写代码挣钱再给你买。

(快用代码实现一下)

回顾工厂模式:

在工厂模式中,每一个工厂负责生产对应的一款具体的产品,具有唯一性。但有时候,人们还需要一个工厂能够提供多个产品对象。

比如上述情景,一个 HW 工厂可以提供手机和音响两个产品,而 XM 工厂也可以提供手机和音响两个产品。

当客户需要购买时,用代码模拟购买过程。

知识快充:

概念1:

产品等级结构:即产品的继承结构。比如一个抽象类(或接口)是手机,其有子类 HW手机、XM 手机。

概念2:

产品族:在抽象工厂模式中,产品族指由同一个工厂生产的位于不同产品等级结构的一组产品。比如 HW 生产的手机和音响。H 手机属于手机产品等级结构,H 音响属于手机产品等级结构。

上述情景引入中,要购买的不是一个产品,就要求工厂生产的具体产品不是一个简单的对象,而是多个位于不同产品等级结构、属于不同类型的具体产品。

应使用 抽象工厂模式

Abstrict Factory Pattern:

Provide an interface for creating families of related or dependent objects without specifying their concrete classes.

不太明白看下面:

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

抽象工厂模式是对象创建型模式,是所有形式的工厂模式中最为抽象和最具一般性的一种形式。

也可以这么理解,具体工厂负责创建一族产品。当某工厂可以生产出不同产品等级的一个产品族中的所有对象时,该模式更高效。

角色:

Factory :抽象工厂

ConcreteFactory :具体工厂

Product :产品

ConcreteProduct :具体产品

类图:

上述类图映射到代码中:

产品:抽象产品 具体产品的代码示例

代码语言:javascript复制
// 抽象产品A
public interface AbstractProductA{
  public void methodA();
}

// 具体产品A1
public class ConcreteProductA1 implememts AbstractProductA{
  public void methodA(){
    // To do ...
  }
}

// 抽象产品B
public interface AbstractProductB{
  public void methodB();
}

// 具体产品B1
public class ConcreteProductB1 implememts AbstractProductB{
  public void methodB(){
    // To do ...
  }
}

工厂:抽象工厂 具体工厂

代码语言:javascript复制
// 抽象工厂
public interface Factory{
  public AbstractProductA createProductA();
  public AbstractProductB createProductB();
}

// 1 号具体工厂
public class ConcreteFactory1 implements Factory{
  public AbstractProductA createProductA(){
      return new ConcreteProductA1();
  }
  
  public AbstractProductB createProductB(){
      return new ConcreteProductB1();
  }
}

客户端代码:

代码语言:javascript复制
···
Factory factory;
AbstractProductA productA;
AbstractProductB productB;
factory = new ConcreteFactory1(); //实例 1 厂
productA = factory.createProductA(); // 1 厂生产 A 产品
productA.algorithmA();

productB = factoru.createProductB(); // 1 厂生产 B 产品
productB.algorithm();

回归到本例代码的解析,两个厂家,都可以生产手机和音响

类图:

首先抽象出两个产品,一个手机,一个音响;

然后画出他们具体的子类。

其次抽象出一个工厂,工厂可以生产两种产品,手机和音响;

然后有两个具体工厂,每一个工厂都可以生产这两种产品,手机和音响;

工厂和产品具有“生产、创建”的关系,具体产品依赖具体工厂。

最后,在客户端使用抽象类存储具体的对象,完美的里氏代换原则。

代码:

第一步:定义抽象产品,手机接口:

代码语言:javascript复制
package com.sample.abstractfactorypattern.product;

public interface Phone {

    public void call();

}

第二步:实现该接口,HWPhone 和 XMPhone:

代码语言:javascript复制
// HWPhone
package com.sample.abstractfactorypattern.product;

public class HWPhone implements Phone {
    @Override
    public void call() {
        System.out.println("正在使用 HWPhone ...");
    }
}

// XMPhone
package com.sample.abstractfactorypattern.product;

public class XMPhone implements Phone{

    @Override
    public void call() {
        System.out.println("正在使用 XMPhone 。。。");
    }
}

第三步:定义音响产品接口

代码语言:javascript复制
package com.sample.abstractfactorypattern.product;

public interface Sound {
      public void play();
}

第四步:实现音响接口,定义 HWSound 类和 XMSound 类:

代码语言:javascript复制
// HWSound
package com.sample.abstractfactorypattern.product;

public class HWSound implements Sound {
    @Override
    public void play() {
        System.out.println("已购买 HWSound 。。。");
    }
}

// XMSound
package com.sample.abstractfactorypattern.product;

public class XMSound implements Sound{

    @Override
    public void play() {
        System.out.println("已购买 XMSound... ");
    }
}

第五步:定义抽象工厂,它包含两个方法,因为有两个产品:

代码语言:javascript复制
package com.sample.abstractfactorypattern.factory;

import com.sample.abstractfactorypattern.product.Phone;
import com.sample.abstractfactorypattern.product.Sound;

public interface Factory {

    public Phone createPhone();
    public Sound createSound();

}

第六步:实现抽象工厂,定义 HWFactory 和 XMFactory,此时返回的即产品对象,可表示 产品对象 被创建成功。

代码语言:javascript复制
// HW 工厂
package com.sample.abstractfactorypattern.factory;

import com.sample.abstractfactorypattern.product.HWPhone;
import com.sample.abstractfactorypattern.product.HWSound;
import com.sample.abstractfactorypattern.product.Phone;
import com.sample.abstractfactorypattern.product.Sound;

public class HWFactory implements Factory {
    @Override
    public Phone createPhone() {
        return new HWPhone();
    }

    @Override
    public Sound createSound() {
        return new HWSound();
    }
}

// XM 工厂
package com.sample.abstractfactorypattern.factory;

import com.sample.abstractfactorypattern.product.Phone;
import com.sample.abstractfactorypattern.product.Sound;
import com.sample.abstractfactorypattern.product.XMPhone;
import com.sample.abstractfactorypattern.product.XMSound;

public class XMFactory implements Factory {
    @Override
    public Phone createPhone() {
        return new XMPhone();
    }

    @Override
    public Sound createSound() {
        return new XMSound();
    }
}

第七步:客户端调用实现

对象都存储在接口类型中,在下面动态进行赋值调用。

当在选定一个工厂时,就代表选择了该品牌产品;当选定哪一个工厂方法时,就代表选择了该方法下的具体产品。

省去了传入一个参数的烦扰,也可以防止参数输入错误,导致拿不到产品。

代码语言:javascript复制
package com.sample.abstractfactorypattern;

import com.sample.abstractfactorypattern.factory.Factory;
import com.sample.abstractfactorypattern.factory.HWFactory;
import com.sample.abstractfactorypattern.factory.XMFactory;
import com.sample.abstractfactorypattern.product.Phone;
import com.sample.abstractfactorypattern.product.Sound;

public class Client {
    public static void main(String[] args){
        Factory factory;
        Phone phone;
        Sound sound;

        // 购买 HW 手机
        factory = new HWFactory();
        phone = factory.createPhone();
        phone.call();

        // 购买 XM 音响
        factory = new XMFactory();
        sound = factory.createSound();
        sound.play();

        // 购买 XM 手机
        phone = factory.createPhone();
        phone.call();

        // 购买 HW 音响
        factory = new HWFactory();
        sound = factory.createSound();
        sound.play();
    }
}

运行效果:

项目结构:

抽象工厂模式优点:

·隔离了具体类的生成

·当一个产品族的多个对象产品被设计成一起工作时,能保证客户端只使用同一个产品族中的对象。

·增加新的产品族比较方便,符合开闭原则。

抽象工厂模式缺点:

·增加新的产品等级结构比较麻烦,需要修改具体工厂、抽象工厂等,违背了开闭原则。

上述的优缺点也体现了抽象工厂模式下开闭原则的倾斜性:

增加新的产品族,符合开闭原则。比如,新增 PG 的手机和音响,那么只需在抽象产品接口下新增两个具体实现类,PGPhone 和 PGSound;同时,在新增一个具体工厂 PGFactory,实现抽象工厂。

而当新增产品等级结构时,不符合开闭原则。比如 HW 和 XM 两家工厂新增产品电脑。就需要新增一个抽象接口类 Computer,以及具体实现类;同时,工厂中,也需要对抽象工厂和具体工厂进行修改,增加 createComputer 方法。

模式的适用环境:

一个系统不依赖于产品类实例如何被创建、组合和表达的细节

系统中有多于一个的产品族,但每次只使用其中某一产品族

属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来

产品等级结构稳定,设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构

好了,我的读者朋友们,今天对于抽象工厂模式的分解与分享就到此结束了。对于我文中列举的例子,我说明白了吗?您感觉怎么样?欢迎与我私下交流。

感谢阅读,感谢陪伴。

表情包来源网络,侵删。

原创不易

0 人点赞