• BCSP-玄子Share-Java框基础_工厂模式/代理模式


    三、设计模式

    3.1 设计模式简介

    • 软件设计中的三十六计
    • 是人们在长期的软件开发中的经验总结
    • 是对某些特定问题的经过实践检验的特定解决方法
    • 被广泛运用在 Java 框架技术中

    3.1.1 设计模式的优点

    • 设计模式是可复用的面向对象软件的基础
    • 可以更加简单方便地复用成功的设计和体系结构
    • 帮助开发者做出有利于系统复用的选择,避免损害系统复用性的设计
    • 使其他开发者更加容易理解其设计思路,便于团队交流

    3.1.2 设计模式分类

    GoF(Gang of Four,四人组)设计模式分为23种

    范围/目的创建型模式结构型模式行为型模式
    类模式工厂方法(类)适配器模板方法解释器
    对象模式单例
    原型
    抽象工厂
    建造者
    代理
    (对象)适配器
    桥接
    装饰
    外观
    享元
    组合
    策略
    命令
    职责链
    状态
    观察者
    中介者
    迭代器
    访问者
    备忘录

    3.1.3 面向对象设计原则

    单一职责原则

    • 一个类应该有且仅有一个引起它变化的原因
    • 一个类应该只负责一个职责

    开闭原则

    • 对扩展开放,对修改关闭

    里氏替换原则

    • 引用基类的地方必须能透明地使用其子类的对象
    • 可以用来判断继承关系是否合理

    依赖倒置原则

    • 依赖于抽象而不依赖于具体实现,针对接口编程

    接口隔离原则

    • 尽量将庞大臃肿的接口拆分成更小更具体的接口
    • 接口中只包含客户感兴趣的方法

    迪米特法则

    • 又称最少知道原则
    • 一个软件实体应当尽可能少地与其他实体发生相互作用

    合成复用原则

    • 尽量使用组合/聚合的方式而不是继承关系达到软件复用的目的
    • 是 has-a 关系

    3.2 简单工厂模式

    如何解决类似“Service与某个具体Dao实现”耦合的问题?

    将创建工作转移出来避免在Service中创建具体的Dao实现类,产生耦合

    简单工厂模式,又叫做静态工厂方法模式,不属于 GoF 的23种设计模式之一,可以理解为工厂模式的一个特殊实现

    3.2.1 简单工厂模式+依赖倒置原则

    依据依赖倒置原则,使用setter方法传递依赖关系,减少Service对工厂类的依赖,降低耦合

    public class NewsServiceImpl implements NewsService {
    	private NewsDao dao;
    	public void setDao(NewsDao dao) {
    		this.dao = dao;
    	}
    	… …
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.2.2 简单工厂+参数

    简单工厂模式可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类

    // 创建NewsDao实例的工厂方法
    public static NewsDao getInstance(String key) {
        switch (key) {
            case "mysql":
                return new NewsDaoMySqlImpl();
            case "oracle":
                return new NewsDaoOracleImpl();
            case "redis":
                return new NewsDaoRedisImpl();
            default:
                throw new RuntimeException("无效的数据库类型:" + key + " ,DAO获取失败");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    要创建的产品不多且逻辑不复杂的情况,可以考虑简单工厂模式

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

    • 工厂(Factory)
    • 抽象产品(Product)
    • 具体产品(Concrete Product)

    增加新的产品需要修改,工厂方法的判断逻辑,不符合开闭原则


    3.3 工厂方法模式

    3.3.1 实现方式

    对简单工厂模式的进一步抽象,工厂方法模式的主要角色如下

    • 抽象产品(Product)
    • 抽象工厂(Abstract Factory)
    • 具体产品(Concrete Product)
    • 具体工厂(Concrete Factory)

    3.3.2 代码案例

    创建抽象工厂接口

    public interface AbstractFactory {
        public NewsDao getInstance();
    }
    
    • 1
    • 2
    • 3

    为不同NewsDao实现创建相对应的具体工厂

    // 以生产NewsDaoMySqlImpl实例的工厂为例
    public class MySqlDaoFactory implements AbstractFactory {
        @Override
        public NewsDao getInstance() {
            return new NewsDaoMySqlImpl();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在测试方法中通过特定工厂生产相关的NewsDao实例

    AbstractFactory factory = new MySqlDaoFactory(); 
    // 改变具体工厂可创建不同产品
    NewsDao dao = factory.getInstance();
    
    • 1
    • 2
    • 3

    3.3.3 优缺点

    优点

    • 只需要知道具体工厂就可得到所要的产品,无须知道产品的具体创建过程
    • 基于多态,便于对复杂逻辑进行封装管理
    • 增加新的产品时无须对原工厂进行任何修改,满足开闭原则

    缺点

    • 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度

    3.4 代理设计模式

    单一职责原则的体现,包含如下角色

    • 抽象主题(Subject)
    • 真实主题(Real Subject)
    • 代理(Proxy)

    实现方式总体上分为静态代理和动态代理

    • 静态代理由开发者针对抽象主题编写相关的代理类实现,编译之后生成代理类的class文件
    • 动态代理是在运行时动态生成的,在运行时动态生成代理类字节码

    3.4.1 基于接口的静态代理实现

    // 抽象主题接口 - 图片
    public interface Image {
        void display();
    }
    
    // 真实主题类 - 真实图片
    public class RealImage implements Image {
        private String filename;
    
        public RealImage(String filename) {
            this.filename = filename;
            loadImageFromDisk();
        }
    
        private void loadImageFromDisk() {
            System.out.println("Loading image from disk: " + filename);
        }
    
        public void display() {
            System.out.println("Displaying image: " + filename);
        }
    }
    
    // 代理类 - 图片代理
    public class ImageProxy implements Image {
        private RealImage realImage;
        private String filename;
    
        public ImageProxy(String filename) {
            this.filename = filename;
        }
    
        public void display() {
            if (realImage == null) {
                realImage = new RealImage(filename);
            }
            realImage.display();
        }
    }
    
    // 调用代码
    public class Client {
        public static void main(String[] args) {
            // 创建代理对象并显示图片
            Image image = new ImageProxy("example.jpg");
            image.display();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    3.4.2 代理模式优点分析

    • 代理模式将客户与目标对象分离,在一定程度上降低了系统的耦合度
    • 代理对象可以对目标对象的功能进行扩展,目标对象和扩展功能职责清晰且不会产生耦合

    3.4.3 动态代理

    静态代理需要手工编写代理类,存在以下弊端

    • 目标对象API发生变化,代理类也必须进行修改,增加工作量且不符合开闭原则
    • 通过继承得到的代理类只能对一种类型进行代理,组件较多时,代理类的开发工作量巨大
    • 动态代理提供了运行时动态扩展对象行为的能力
    • 能够依据给定的业务规则,在运行时动态生成代理类

    3.4.4 JDK 动态代理

    从JDK 1.3版本开始引入

    是面向接口的代理实现

    • 要求被代理的目标对象必须通过抽象主题接口进行定义

    核心API

    • java.lang.reflect.InvocationHandler接口
      • 代理方法的调用处理程序,负责为代理方法提供业务逻辑
      • 包含方法:Object invoke(Object proxy, Method method, Object[] args)
    • java.lang.reflect.Proxy类
      • 负责动态创建代理类及其实例
      • 主要方法:static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

    3.4.5 CGLIB 动态代理

    如果被代理的目标对象不是通过接口进行定义的,JDK 动态代理将无法实施

    • CGLIB(Code Generation Library)是一个功能强大,高性能的代码生成库
    • 可以为没有实现接口的类提供代理,原理是为需要代理的类动态生成一个子类作为其代理类

    需要使用继承和重写机制,CGLIB动态代理对于final类或final方法无能为力

    cglib https://github.com/cglib/cglib/releases下载所需的 jar 文件

    • cglib-nodep-x.x.x.jar

    主要 API

    • net.sf.cglib.proxy.MethodInterceptor 接口
      • 负责拦截父类的方法调用,以便加入代理的业务逻辑
      • 包含方法
        • Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
    • net.sf.cglib.proxy.Enhancer 类
      • 负责动态创建代理类及其实例
      • 主要方法
        • setSupperclass()
        • setCallback()
        • set…
        • create()

    3.4.6 JDK 和 CGLIB 动态代理的对比

    • JDK 动态代理面向接口代理,只能对基于接口设计的目标对象进行代理
    • CGLIB 动态代理可以通过继承方式实现,不依赖接口,但是不能代理 final 的类和方法

  • 相关阅读:
    modbus报文
    Linux系统上安装软件
    9.nginx代理
    怎么把家里闲置旧苹果手机变成家用安防监控摄像头
    Devkit代码迁移工具——smartdenovo源码迁移
    解放双手!无需注解快速生成API文档,跟SpringBoot绝配!
    Flet教程之 11 Row组件在水平数组中显示其子项的控件 基础入门(教程含源码)
    javaIO流03:InputStream字节输入流和 FileInputStream详解
    【HCIE】13.VXLAN EVPN
    CSS 四中方法实现水平 垂直居中
  • 原文地址:https://blog.csdn.net/qq_62283694/article/details/132683615