你还在看错误的抽象工厂模式实现案例?

昨天在搜抽象工厂模式时,发现有好几篇博文讲的实现方式和我所认知的有出入,而且还看到某某教程讲的也是错误的还在搜索引擎排第一。

大家讲的简单工厂模式和工厂方法模式都是没问题的,关键到抽象工厂模式就不对了。

先从简单工厂模式和工厂方法模式复习一下,然后再看看错的是咋讲的。已经对抽象工厂模式烂熟于心的请忽略此文。

简单工厂模式:

有个一公共的产品接口,定义一些方法,各个具体的产品类都实现这个接口,然后有一个专门生产产品类实例的类被称作工厂类,专门为客户端生产产品实例,这个工厂的内部实现就是使用switch或ifelse进行逻辑判断实现的,根据客户端传递的参数不同,来决定什么时候创建什么样的具体产品,生产出不同的具体产品返回给客户端。这样,这个工厂类就集成了所有产品的创建逻辑,它变得无所不知,这也让它变成了核心。但这也成为了他的缺点,因为它将所有的逻辑集中放在一个类里,当产品接口有了新的产品实现类,工厂类需要增加代码,判断在什么时候创建该产品,就需要加入新的判断逻辑。

 ShoesFactory工厂

public class ShoesFactory{
    public static final String NIKE = "NIKE";
    public static final String ADIDAS = "ADIDAS";
    public static Shoes getShoes(String brand) {
        Shoes shoes = null;
        switch (brand) {
            case "NIKE":
                shoes = new NikeShoes();
                break;
            case "ADIDAS":
                shoes = new AdidasShoes();
                break;
            default:
                shoes = null;
        }
        return shoes;
    }
}

 测试类

public class SimpleFactoryTest{
    public void main(String arg[]){
        Shoes nikeShoes = ShoesFactory.getShoes(ShoesFactory.NIKE);
        nikeShoes.desc();
        Shoes adidasShoes = ShoesFactory.getShoes(ShoesFactory.ADIDAS);
        adidasShoes.desc();
    }
}

 

缺点:每增加一个产品,都要新写一个类,然后在工厂类里增加分支,不符合java的开闭原则。

优点:对外屏蔽产品实例的创建,客户端只负责“消费”,通过这种方式实现了对职责的分割。

工厂方法模式:

有一个公共的产品接口和一个公共的产品工厂接口,任何具体工厂必须实现这个工厂接口。将具体的产品的实例创建任务交给具体的工厂类,工厂的职责单一化,每个产品实例从公共的工厂类内抽离出来,降低了耦合度。这样,当增加新的产品实例时,只需要增加相应的工厂类,符合开闭原则。

 
ShoesFactory
public interface ShoesFactory{
    Shoes getShoes();
}

 NikeShoesFactory 

public class NikeShoesFactory implements ShoesFactory{
    @Override
    public Shoes getShoes() {
        return new NikeShoes();
    }
}

 AdidasShoesFactory 

public class AdidasShoesFactory implements ShoesFactory{
    @Override
    public Shoes getShoes() {
        return new AdidasShoes();
    }
}

 

当增加李宁鞋子时只需增加LiningShoes使其实现Shoes接口,然后增加LiningShoesFactory工厂类实现ShoesFactory接口使其负责生产LiningShoes的实例。

缺点:没增加一个具体产品都要增加相应的工厂类,增加代码量。

优点:符合开闭原则,易于扩展。

抽象工厂模式:

当有多个类型的产品时,产品类型之间横向发展,如增加裤子、上衣等产品。在工厂方法模式里,只有一个产品接口类鞋子,各种鞋子如耐克鞋、阿迪鞋实现鞋子接口,而工厂类只需要写2个就行了,耐克鞋工厂、阿迪鞋工厂类,各个工厂类职责单一只生产对应的一种具体产品。

而此时若增加了新的产品类型如裤子,他的具体产品阿迪裤子、耐克裤子,这个时候工厂类怎么实现呢?还像工厂方法类那样吗?如果业务上要求单一工厂原则(一个工厂只生产一个类型的产品),继续使用工厂方法没有问题,但如果需要提供一些列产品显然使用工厂方法模式是不行的。

此时随着多个产品类型的增加,产品之间形成一个个的产品族(耐克鞋、阿迪鞋是一个产品族,耐克裤子、阿迪裤子是一个产品族),而不同品牌之间形成产品等级。

网文实现方式

当我在网上搜索【抽象工厂模式】时,发现有不少文章给出的实现方式是如下图这样的。

抽象工厂模式结构:

 

factory 包里放的是抽象工厂类和它的具体工厂类 PantsFactory 裤子工厂,ShoesFactory 鞋子工厂,而抽象工厂类里有两个方法 getPants 和 getShoes,所以对于这两个工厂来说,继承抽象工厂后都会有一个空的方法实现返回null,因为具体的工厂只生产某一产品,即裤子厂只生产裤子,归于鞋子是空实现。

 

代码实现

抽象工厂类

public abstract class AbstractFactory2{
    public abstract Shoes getShows(String brand);
    public abstract Pants getPants(String brand);
}

 PantsFactory 裤子工厂

public class PantsFactory extends AbstractFactory2{
    //裤子工厂不能生产鞋子,空实现 返回null
    @Override
    public Shoes getShows(String brand) {
        return null;
    }

    @Override
    public Pants getPants(String brand) {
        Pants pants = null;
        switch (brand) {
            case "NIKE":
                pants = new NikePants();
                break;
            case "ADIDAS":
                pants = new AdidasPants();
                break;
            default:
                pants = null;
        }
        return pants;
    }
}

 

ShoesFactory 鞋子工厂
public class ShoesFactory extends AbstractFactory2{
    @Override
    public Shoes getShows(String brand) {
        Shoes shoes = null;
        switch (brand) {
            case "NIKE":
                shoes = new NikeShoes();
                break;
            case "ADIDAS":
                shoes = new AdidasShoes();
                break;
            default:
                shoes = null;
        }
        return shoes;
    }
    //鞋子工厂不能生产鞋子,空实现 返回null
    @Override
    public Pants getPants(String brand) {
        return null;
    }
}

 

FactoryProductor2工厂生产者,通过不同的产品名称创建具体工厂实例:(这里貌似简单工厂模式)
public class FactoryProductor2{
    public static final String SHOES = "SHOES";
    public static final String PANTS = "PANTS";

    public static AbstractFactory2 getFactory(String productType) {
        if (SHOES.equalsIgnoreCase(productType)) {
            return new ShoesFactory();
        } else if (PANTS.equalsIgnoreCase(productType)) {
            return new PantsFactory();
        }
        return null;
    }
}

 测试类

public class AbstractFactoryTest2{
    public static void main(String[] args) {
        AbstractFactory2 factory = FactoryProductor2.getFactory(FactoryProductor2.PANTS);
        Pants nikePants = factory.getPants("NIKE");
        Pants adidasPants = factory.getPants("ADIDAS");
        nikePants.desc();
        adidasPants.desc();

        factory = FactoryProductor2.getFactory(FactoryProductor2.SHOES);
        Shoes nikeShoes = factory.getShows("NIKE");
        Shoes adidasShows = factory.getShows("ADIDAS");
        nikeShoes.desc();
        adidasShows.desc();
    }
}

 

NikePants
AdidasPants
NikeShoes
AdidasShoes

 

分析正确性

大家看这样的抽象工厂,每次创建产品都要知道产品的品牌,而且工厂类的另外一个方法被废弃。

还美名其曰的说产品族难扩展,产品等级易扩展。那我们就来看看在这种方式下是怎么个难易法。

产品族难扩展: 当我们想增加上衣Coat产品时,按现在的方式扩展一下看看。首先我们需要新建接口Coat,新建实现类NikeCoat和AdidasCoat实现Coat接口。然后我们需要在抽象工厂类增加创建Coat产品的方法,同时修改现有的2个工厂完成Coat相关实现类的实例创建逻辑,其次我们需要在FactoryProductor2增加分支逻辑创建CoatFactory。

撇开FactoryProductor2类不说,总结下我们都做了什么:

  • 新增产品接口Cost
  • 修改抽象工厂,新增getCoat方法,修改原有工厂增加getCoat的空实现
  • 新增具体工厂CoatFactory

产品等级易扩展: 当我们想增加李宁品牌时,我们需要做什么?pants包下创建LiNingPants实现pants接口,shoes包下创建LiNingShoes实现Shoes接口,然后修改factory包下的ShoesFactory工厂和PantsFactory工厂,各自增加分支判断创建李宁牌的鞋子和裤子。

总结下我们都做了什么:

  • 新增具体产品 LiNingPants 和 LiNingShoes

  • 修改现有具体工厂类 ShoesFactory 工厂和 PantsFactory 工厂,增加创建李宁工厂分支

这样看来产品族扩展起来确实难,而且不符合开闭原则,但产品等级扩展起来却也不容易,也不符合开闭原则。

大家发现没有,现在的抽象工厂的具体工厂内部是怎么实现的?还是使用switch或ifesle分支,不论扩展产品族还是产品等级都需要改具体工厂类,增加分支,都不符合开闭原则。这是正确的抽象工厂??

NO!!

这样实现抽象工厂模式的不仅仅是搜到的几篇博文,而且还有*鸟教程,搜索排第一:)

 

正确的抽象工厂

看下这个图,发现具体工厂的名字变了,分别为AdidasFactory和NikeFactory,可想而知,以品牌命名具体的工厂只创建本品牌下的所有产品。

代码实现

抽象工厂类

public abstract class AbstractFactory{
    public abstract Shoes getShows();
    public abstract Pants getPants();
}

这样,每一个具体的工厂类,实现具体方法时,每个方法都能生成对应的产品,方法没有空实现。而且每一个具体工厂内部没有判断分支。

NikeFactory耐克工厂

public class NikeFactory extends AbstractFactory{
    @Override
    public Shoes getShows() {
        return new NikeShoes();
    }
    @Override
    public Pants getPants() {
        return new NikePants();
    }
}

 

AdidasFactory阿迪工厂

public class AdidasFactory extends AbstractFactory{
    @Override
    public Shoes getShows() {
        return new AdidasShoes();
    }
    @Override
    public Pants getPants() {
        return new AdidasPants();
    }
}

 

而对于工厂生产者来说,通过不同的品牌名称创建对应的工厂实例,

public class FactoryProductor{
    public static final String NIKE = "NIKE";
    public static final String ADIDAS = "ADIDAS";

    public static AbstractFactory getFactory(String brand) {
        if (NIKE.equalsIgnoreCase(brand)) {
            return new NikeFactory();
        } else if (ADIDAS.equalsIgnoreCase(brand)) {
            return new AdidasFactory();
        }
        return null;
    }
}

 测试类

public class AbstractFactoryTest{
    public static void main(String[] args) {
        AbstractFactory nikeFactory = FactoryProductor.getFactory(FactoryProductor.NIKE);
        Pants nikePants = nikeFactory.getPants();
        Shoes nikeShows = nikeFactory.getShows();
        nikePants.desc();
        nikeShows.desc();

        AbstractFactory adidasFactory = FactoryProductor.getFactory(FactoryProductor.ADIDAS);
        Shoes adidasShows = adidasFactory.getShows();
        Pants adidasPants = adidasFactory.getPants();
        adidasPants.desc();
        adidasShows.desc();

    }
}
NikePants
NikeShoes
AdidasPants
AdidasShoes

分析

现在我们再来看一下,所谓的产品族难扩展,产品等级易扩展。

产品族难扩展: 当增加cost上衣产品时,首先需要新建包coat,新建接口Coat,新建实现类NikeCoat和AdidasCoat,然后在抽象工厂类新增抽象方法getCoat,ShoesFactory工厂和PantsFactory工厂分别实现该方法。

都做了什么:新建产品接口Coat,并创建实现类,抽象工厂新增抽象方法,原具体工厂类重写新增的抽象方法。

产品等级易扩展: 当增加新的品牌时,比如增加李宁牌的鞋子和裤子,怎么扩展?首先创建LiningShoes和LiningPants类分别实现Shoes和Pants接口,然后新建LiningFactory工厂类使其继承AbstractFactory抽象工厂类,重写getShoes和getPants方法。

都做了什么:新增产品实现类,新增具体工厂类。

类图

 

增加产品族

增加后,这个系统中有三个产品族,两个产品等级

增加产品等级

增加后,这个系统中2个产品族,3个产品等级

 


作者:walkinger
链接:https://juejin.im/post/5d4b8de1e51d453b1f37eac4


posted @ 2019-08-08 16:13  问北  阅读(900)  评论(4编辑  收藏  举报