zoukankan      html  css  js  c++  java
  • 设计模式学习笔记(五):工厂方法模式

    1 概述

    1.1 引言

    尽管简单工厂模式实现了对象的创建和使用分离,但是仍然存在以下两个问题:

    • 工厂类过于庞大,包含了大量的判断代码,导致维护和测试难度增大
    • 系统扩展不灵活,如果增加了新的产品类型,必须修改静态工厂方法的业务逻辑,违反了开闭原则
    • 具体产品与工厂类之间的耦合度高,严重影响了系统的灵活性和扩展性

    一个更好的办法是使用工厂方法模式。

    1.2 定义

    工厂方法模式:定义一个用于创建对象的接口,让子类决定将哪一个类实例化。

    工厂方法又简称工厂模式或虚拟构造器模式或多态工厂模式,让一个类的实例化延迟到其子类,是一种类创建型模式。

    1.3 结构图

    在这里插入图片描述

    1.4 角色

    • Product(抽象产品):定义产品的接口,是工厂方法模式所创建的超类型,也就是产品对象的公共父类
    • ConcreteProduct(具体产品):实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,具体工厂与具体产品一一对应
    • Factory(抽象工厂):在抽象工厂类中,声明了工厂方法,用于返回一个产品。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口
    • ConcreteFactory(具体工厂):它是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实例

    2 典型实现

    2.1 步骤

    • 定义抽象产品:定义为接口/抽象类,是所有具体产品的父类
    • 定义具体产品:实现/继承抽象产品,一个类对应与一个具体产品
    • 定义抽象工厂:定义为接口/抽象类,声明工厂方法,但不需要实现,在运行时确定具体工厂
    • 定义具体工厂:实现/继承抽象工厂,实现其中的工厂方法

    2.2 抽象产品

    这里定义为接口:

    interface Product
    {
        void method();
    }
    

    2.3 具体产品

    两个示例具体产品:

    class ConcreteProductA implements Product
    {
        public void method()
        {
            System.out.println("Concrete Product A");
        }
    }
    
    class ConcreteProductB implements Product
    {
        public void method()
        {
            System.out.println("Concrete Product B");
        }
    }
    

    2.4 抽象工厂

    这里定义为接口:

    interface Factory
    {
        Product get();
    }
    

    抽象工厂中声明了工厂方法但没有实现,交由子类具体工厂负责。

    2.5 具体工厂

    class ConcreteFactoryA implements Factory
    {
        public Product get()
        {
            return new ConcreteProductA();
        }
    }
    
    class ConcreteFactoryB implements Factory
    {
        public Product get()
        {
            return new ConcreteProductB();
        }
    }
    

    一个具体产品对应一个具体工厂,每一个具体工厂返回不同的具体产品。

    2.6 客户端

    客户端针对抽象产品以及抽象工厂进行编程,无需知道具体产品的类名,只需要知道具体产品对应的工厂,即可获取具体产品:

    Factory factory = new ConcreteFactoryA();
    Product product = factory.get();
    product.method();
    factory = new ConcreteFactoryB();
    product = factory.get();
    product.method();
    

    3 实例

    日志记录器的设计:该记录器可以通过多种途径保存系统的运行日志,例如文件记录或者数据库记录,使用工厂方法模式进行设计。

    设计如下:

    • 抽象产品:Logger
    • 具体产品:DatabaseLogger+FileLogger
    • 抽象工厂:LoggerFactory
    • 具体工厂:DatabaseLoggerFactory+FileLoggerFactory

    代码如下:

    public class Test
    {
        public static void main(String[] args) {
            LoggerFactory factory = new FileLoggerFactory();        
            Logger logger = factory.createLogger();
            logger.log();
        }
    }
    
    //抽象产品
    interface Logger
    {
        void log();
    }
    
    //具体产品:DatabaseLogger
    class DatabaseLogger implements Logger
    {
        public void log()
        {
            System.out.println("数据库日志记录");
        }
    }
    
    //具体产品:FileLogger
    class FileLogger implements Logger
    {
        public void log()
        {
            System.out.println("文件日志记录");
        }
    }
    
    //抽象工厂
    interface LoggerFactory
    {
        Logger createLogger();
    }
    
    //具体工厂:DatabaseLoggerFactory
    class DatabaseLoggerFactory implements LoggerFactory
    {
        public Logger createLogger()
        {
            return new DatabaseLogger();
        }
    }
    
    //具体工厂:FileLoggerFactory
    class FileLoggerFactory implements LoggerFactory
    {
        public Logger createLogger()
        {
            return new FileLogger();
        }
    }
    

    4 隐藏

    可以把抽象工厂设置为抽象类,工厂方法直接可以对客户端隐藏,也就是说可以直接通过抽象工厂调用具体产品类的业务方法,客户端无需创建具体产品,直接通过工厂类调用即可,代码修改如下(抽象产品以及具体产品类不用修改):

    //抽象工厂
    abstract class LoggerFactory
    {
        public void log()
        {
            this.createLogger().log();
        }
        public abstract Logger createLogger();
    }
    
    //具体工厂:DatabaseLoggerFactory
    class DatabaseLoggerFactory extends LoggerFactory
    {
        public Logger createLogger()
        {
            return new DatabaseLogger();
        }
    }
    
    //具体工厂:FileLoggerFactory
    class FileLoggerFactory extends LoggerFactory
    {
        public Logger createLogger()
        {
            return new FileLogger();
        }
    }
    
    public class Test
    {
        public static void main(String[] args) {
            LoggerFactory factory = new FileLoggerFactory();
            factory.log();
        }
    }
    

    5 主要优点

    • 封装细节:工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需关心所需产品对应的工厂,无需关心创建细节,甚至无须知道具体产品类的类名
    • 多态:工厂方法的多态性能够让工厂可以自主确定创建何种产品对象,而如何创建对象的细节则完全封装在具体工厂内部
    • 扩展性好:加入新产品时无须修改抽象工厂,抽象产品的接口,也无须修改客户端与其他的具体产品和具体工厂,只需要增加一个具体工厂以及具体产品,系统扩展性很好,完全符合开闭原则

    6 主要缺点

    • 类数量多:在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,一定程度上增加了系统的复杂度,有更多的类需要编译和运行,给系统带来额外开销
    • 增加理解难度:基于系统的扩展性需要引入抽象层,在客户端中均使用了抽象层的定义,增加了系统的抽象性以及理解难度

    7 适用场景

    • 客户端不知道其所需要的对象的类:在工厂方法模式中,客户端不需要知道具体的产品类名,只需要知道所对应的工厂即可
    • 抽象工厂类通过子类来指定创建哪个对象:工厂方法模式中,抽象工厂类只需要提供一个创建产品的接口,而有其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更加容易扩展

    8 总结

    在这里插入图片描述

    如果觉得文章好看,欢迎点赞。

    同时欢迎关注微信公众号:氷泠之路。

    在这里插入图片描述

  • 相关阅读:
    MYSQL 之 JDBC(十三):处理事务
    MYSQL 之 JDBC(十四):批量处理JDBC语句提高处理效率
    MYSQL 之 JDBC(十五):数据库连接池
    MYSQL 之 JDBC(十六): DBUtils
    MYSQL 之 JDBC(十七): 调用函数&存储过程
    Android初级教程理论知识(第八章网络编程二)
    android6.0SDK 删除HttpClient的相关类的解决方法
    iOS下WebRTC音视频通话(三)-音视频通话
    love~LBJ,奥布莱恩神杯3
    Android简易实战教程--第二话《两种进度条》
  • 原文地址:https://www.cnblogs.com/6b7b5fc3/p/13325423.html
Copyright © 2011-2022 走看看