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


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

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

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

    简单工厂模式:

    有个一公共的产品接口,定义一些方法,各个具体的产品类都实现这个接口,然后有一个专门生产产品类实例的类被称作工厂类,专门为客户端生产产品实例,这个工厂的内部实现就是使用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


  • 相关阅读:
    2020.5.28.第十三周java上机练习
    2020.5.22第十二周java作业
    2020.5.21.第十二周java上机练习
    2020.5.15.java第十一周作业
    2020.5.14.第十一周上机练习
    leetcode02大数相加
    leetcode算法题01
    近期wxss总结
    近期Freecodecamp问题总结
    freecodecamp数字转化成罗马数字
  • 原文地址:https://www.cnblogs.com/ibigboy/p/11321837.html
Copyright © 2020-2023  润新知