其他分享
首页 > 其他分享> > 【设计模式自习室】理解工厂模式的三种形式

【设计模式自习室】理解工厂模式的三种形式

作者:互联网

 

前言

《设计模式自习室》系列,顾名思义,本系列文章带你温习常见的设计模式。主要内容有:

该系列会逐步更新于我的博客和公众号(博客见文章底部)

也希望各位观众老爷能够关注我的个人公众号:后端技术漫谈,不会错过精彩好看的文章。

系列文章回顾

创建型——简单工厂/工厂模式/抽象工厂

引子

工厂模式是一个非常重要的创建型模式,但是工厂模式又分为好多种,并且网上文章很多,很多对工厂模式的定义都不是很明确,甚至还互相冲突,本文希望通过放在一起串讲的形式,力求能够用最简洁的语言理清工厂模式。

先看一个工厂模式的定义:

“Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.”(在基类中定义创建对象的一个接口,让子类决定实例化哪个类。工厂方法让一个类的实例化延迟到子类中进行。)

使用了工厂模式,我们可以将对象的创建和使用分离。用来防止用来实例化一个类的数据和代码在多个类中到处都是。

工厂模式最主要的形式是以下三种:

意图

1. 简单/静态工厂(Simple Factory)

先来看简单工厂模式,它指的是,在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。

在简单工厂模式中,可以根据参数的不同返回不同类的实例。

2. 工厂方法(Factory Method)

工厂方法又可以称为:

工厂模式通过工厂子类来确定究竟应该实例化哪一个具体产品类。不再设计一个工厂类来统一负责所有产品的创建,而是将具体产品的创建过程交给专门的工厂子类去完成。

这一特点无疑使得工厂方法模式具有超越简单工厂模式的优越性,更加符合“开闭原则”。

3. 抽象工厂(Abstract Factory)

在了解抽象工厂之前,我们先要了解下什么是产品等级结构和产品族

产品族 :在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。

产品等级结构 :产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。

工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。

抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态。

如果看到这里还是对抽象工厂理解不够,不要着急,下方的代码示例会给你加深理解。

类图

如果看不懂UML类图,可以先粗略浏览下该图,想深入了解的话,可以继续谷歌,深入学习:

1. 简单/静态工厂(Simple Factory)

简单工厂模式包含如下角色:

2. 工厂方法(Factory Method)

(相比简单工厂,将工厂变为了抽象工厂和具体工厂)

3. 抽象工厂(Abstract Factory)

抽象工厂模式包含如下角色:

你会发现工厂模式和抽象工厂的角色是相同的。

时序图

时序图(Sequence Diagram)是显示对象之间交互的图,这些对象是按时间顺序排列的。时序图中显示的是参与交互的对象及其对象之间消息交互的顺序。

我们可以大致浏览下时序图,如果感兴趣的小伙伴可以去深究一下:

1. 简单/静态工厂(Simple Factory)

2. 工厂方法(Factory Method)

3. 抽象工厂(Abstract Factory)

代码样例

给出的代码中,每个类都以角色来区分

1. 简单/静态工厂(Simple Factory)

代码来自:

https://www.jianshu.com/p/d1b6731c1c0e

工厂——LoginManager
public class LoginManager {
    public static Login factory(String type){
        if(type.equals("password")){
            
            return new PasswordLogin();
            
        }else if(type.equals("passcode")){
            
            return new DomainLogin();
            
        }else{
            /**
             * 这里抛出一个自定义异常会更恰当
             */
            throw new RuntimeException("没有找到登录类型");
        }
    }
}
抽象产品——Login接口
public interface Login {
    //登录验证
    public boolean verify(String name , String password);
}
具体产品——PasswordLogin
public class PasswordLogin implements Login {

    @Override
    public boolean verify(String name, String password) {
        // TODO Auto-generated method stub
        /**
         * 业务逻辑
         */
        return true;
    }
}

客户端调用

public class Test {
    public static void main(String[] args) {
        String loginType = "password";
        String name = "name";
        String password = "password";
        Login login = LoginManager.factory(loginType);
        boolean bool = login.verify(name, password);
        if (bool) {
            /**
             * 业务逻辑
             */
        } else {
            /**
             * 业务逻辑
             */
        }
    }
}

假如不使用上面的简单工厂模式则验证登录Servlet代码如下,可以看到代码耦合度很高:

public class Test {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        String loginType = "password";
        String name = "name";
        String password = "password";
        //处理口令认证
        if(loginType.equals("password")){
            PasswordLogin passwordLogin = new PasswordLogin();
            boolean bool = passwordLogin.verify(name, password);
            if (bool) {
                /**
                 * 业务逻辑
                 */
            } else {
                /**
                 * 业务逻辑
                 */
            }
        }
        //处理域认证
        else if(loginType.equals("passcode")){
            DomainLogin domainLogin = new DomainLogin();
            boolean bool = domainLogin.verify(name, password);
            if (bool) {
                /**
                 * 业务逻辑
                 */
            } else {
                /**
                 * 业务逻辑
                 */
            }    
        }else{
            /**
             * 业务逻辑
             */
        }
    }
}

2. 工厂方法(Factory Method)

代码来自:https://www.jianshu.com/p/1cf9859e0f7c

(相比简单工厂,将工厂变为了抽象工厂和具体工厂)

抽象的产品接口——ILight:具备开关两种功能的产品

public interface ILight
    {
        void TurnOn();
        void TurnOff();
    }

具体的产品类——BulbLight

 public class TubeLight implements ILight
    {
        public void TurnOn()
        {
            Console.WriteLine("TubeLight turns on.");
        }

        public void TurnOff()
        {
            Console.WriteLine("TubeLight turns off.");
        }

    }

抽象的工厂类——ICreator

public interface ICreator
    {
        ILight CreateLight();
    }

具体的工厂类——BulbCreator

 public class BulbCreator implements ICreator
    {
        public ILight CreateLight()
        {
            return new BulbLight();
        }

    }

客户端调用

static void Main(string[] args)
        {
            //先给我来个灯泡
            ICreator creator = new BulbCreator();
            ILight light = creator.CreateLight();
            light.TurnOn();
            light.TurnOff();

            //再来个灯管看看
            creator = new TubeCreator();
            light = creator.CreateLight();
            light.TurnOn();
            light.TurnOff();
        }

本例中每个具体工厂类只负责生产一种类型的产品,当然每个具体工厂类也内部可以维护少数几种产品实例对象,类似于简单工厂模式。

3. 抽象工厂(Abstract Factory)

代码来自:https://www.jianshu.com/p/d6622f3e71ed

抽象产品: 苹果系列

public interface Apple
     {
        void AppleStyle();
    }

抽象产品: 三星系列

public interface Sumsung
     {
        void BangziStyle();
   }

具体产品:iphone

public class iphone implements Apple
     {
         public void AppleStyle()
         {
            Console.WriteLine("Apple's style: iPhone!");
        }
     }

具体产品:ipad

 public class ipad implements Apple
    {
 
         public void AppleStyle()
        {
             Console.WriteLine("Apple's style: iPad!");
        }
 
     }

具体产品:note2

public class note2 implements Sumsung
     {
         public void BangziStyle()
         {
            Console.WriteLine("Bangzi's style : Note2!");
         }
 
     }

抽象工厂

public interface Factory
     {
         Apple createAppleProduct();
         Sumsung createSumsungProduct();
     }

手机工厂

public class Factory_Phone implements Factory
     {
         public Apple createAppleProduct()
         {
             return new iphone();
         }
 
         public Sumsung createSumsungProduct()
         {
             return new note2();
         }
     }

pad工厂

public class Factory_Pad implements  Factory
     {
         public Apple createAppleProduct()
         {
             return new ipad();
         }
 
         public Sumsung createSumsungProduct()
         {
             return new Tabs();
         }
     }

客户端调用

public static void Main(string[] args)
         {
              //采购商要一台iPad和一台Tab
              Factory factory = new Factory_Pad();
              Apple apple = factory.createAppleProduct();
              apple.AppleStyle();
              Sumsung sumsung = factory.createSumsungProduct();
              sumsung.BangziStyle();
  
             //采购商又要一台iPhone和一台Note2
             factory = new Factory_Phone();
             apple = factory.createAppleProduct();
             apple.AppleStyle();
             sumsung = factory.createSumsungProduct();
             sumsung.BangziStyle();
         }

下面的代码是刚才已经看过的工厂模式的调用代码,对比下,发现区别了吗?工厂方法只需要管是哪个产品族,而抽象工厂还要考虑产品的等级结构,也就是他是苹果造的,还是三星造的。尽管他们都是手机!

static void Main(string[] args)
        {
            //先给我来个灯泡
            ICreator creator = new BulbCreator();
            ILight light = creator.CreateLight();
            light.TurnOn();
            light.TurnOff();

            //再来个灯管看看
            creator = new TubeCreator();
            light = creator.CreateLight();
            light.TurnOn();
            light.TurnOff();
        }

用意图里面的话再次总结一下:

工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。

优缺点

1. 简单/静态工厂(Simple Factory)

优点
缺点

2. 工厂方法(Factory Method)

优点
缺点

3. 抽象工厂(Abstract Factory)

优点
缺点

开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦

使用场景举例

1. 简单/静态工厂(Simple Factory)

工厂类负责创建的对象比较少:由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。

Java JDK中使用
  1. JDK类库中广泛使用了简单工厂模式,如工具类java.text.DateFormat,它用于格式化一个本地日期或者时间。
public final static DateFormat getDateInstance();
public final static DateFormat getDateInstance(int style);
public final static DateFormat getDateInstance(int style,Locale
locale);
  1. Java加密技术
    获取不同加密算法的密钥生成器:
KeyGenerator keyGen=KeyGenerator.getInstance("DESede");
  1. 创建密码器:
Cipher cp = Cipher.getInstance("DESede");

2. 工厂方法(Factory Method)

Java JDK中使用
Connection conn=DriverManager.getConnection("jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=DB;user=sa;password=");
Statement statement=conn.createStatement();
ResultSet rs=statement.executeQuery("select * from UserInfo");

3. 抽象工厂(Abstract Factory)

在以下情况下可以使用抽象工厂模式:

Java JDK中使用

总结

抽象工厂模式中我们可以定义实现不止一个接口,一个工厂也可以生成不止一个产品类,抽象工厂模式较好的实现了“开放-封闭”原则,是三个模式中较为抽象,并具一般性的模式。

但是归根结底,工厂模式还是一定程度上增加了代码复杂度,有没有一种办法,不需要创建工厂,也能解决代码以后的扩展性问题呢?

答案是有的,通过控制反转(ioc),对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。(DI),也就是Spring最核心的思想了。大家可以自行查阅Spring思想的文章。

话说,最近也真的想总结一下Spring源码相关的知识,正在学习中。

参考

关注我

我是一名后端开发工程师。

主要关注后端开发,数据安全,爬虫,物联网,边缘计算等方向,欢迎交流。

各大平台都可以找到我

原创博客主要内容

个人公众号:后端技术漫谈

 

标签:Factory,模式,自习室,三种,产品,工厂,抽象,设计模式,public
来源: https://blog.51cto.com/u_15270272/2912061