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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值