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;
	}
	… …
}

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获取失败");
    }
}

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

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

  • 工厂(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();
}

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

// 以生产NewsDaoMySqlImpl实例的工厂为例
public class MySqlDaoFactory implements AbstractFactory {
    @Override
    public NewsDao getInstance() {
        return new NewsDaoMySqlImpl();
    }
}

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

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

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();
    }
}

BCSP-玄子Share-Java框基础_工厂模式/代理模式_第1张图片

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 的类和方法

你可能感兴趣的:(JAVASE,+,JAVAOOP,java,代理模式,开发语言)