设计模式之装饰器、代理、工厂方法模式

引言

原文链接:带你读懂几种常见的设计模式 第二弹 希望点进去的小伙伴关注一下我的公众号哟,文末有二维码,谢谢!

今天的文章继续介绍设计模式。上一弹中介绍了简单工厂模式和策略模式,本文将介绍装饰器模式、代理模式和工厂方法模式。

1、装饰器模式

装饰模式的思想是:层层包裹。包裹就是装饰,那到底什么是层层包裹呢?有什么实际例子吗?

java的io模块中采用了这一种思想,比如下面这段代码。

BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("F:/test.txt")));

BufferedReader包裹了InputStreamReader,而InputStreamReader又包裹了FileInputStream。

首先,FileInputStream具有读取文件字节流的能力,在其外面套了一层InputStreamReader后,让其具有读取字符流的能力,再在外面套一层BufferedReader后,又让其拥有了缓冲读取的能力。

上面是实际例子,下面我来抽象一下装饰器模式中有哪些角色。

  • 核心能力接口

  • 具有核心能力的核心组件(实现了核心能力接口)

  • 装饰器(其本质也是一个具有核心能力的组件,特别之处在于它可以再容纳一个组件)

  • 具有装饰能力的组件(继承了装饰器)

下面通过代码来实现一下这些角色。

核心能力接口。

public interface Core {
    void sayHello();
}

核心组件。

public class CoreComponent implements Core {

    @Override
    public void sayHello() {
        System.out.println("CoreComponent say Hello");
    }
}

装饰器。

Decorator类是实现装饰器模式的核心。Decorator内置了一个Core类型的属性,而且Decorator也是Core类型,这就是包裹。并且Decorator类的sayHello方法并没有提供自己的实现,而是直接调用了内置Core属性的实现。

public abstract class Decorator implements Core {
    private Core component;

    public void setComponent(Core component) {
        this.component = component;
    }

    public Core getComponent() {
        return component;
    }

    @Override
    public void sayHello() {
        this.component.sayHello();
    }
}

具有装饰能力的组件A和B。

这两个组件分别提供了自己的实现。

public class DecoratorA extends Decorator{
    private void sayWhat(){
        System.out.println("DecoratorA say what are you doing?");
    }

    public DecoratorA() {
    }
    public DecoratorA(Core component) {
        this.setComponent(component);
    }


    @Override
    public void sayHello() {
        super.sayHello();
        sayWhat();
    }

}
public class DecoratorB extends Decorator{
    private void sayHow(){
        System.out.println("DecoratorB say How are you?");
    }

    public DecoratorB() {
    }
    public DecoratorB(Core component) {
        this.setComponent(component);
    }
    @Override
    public void sayHello() {
        super.sayHello();
        sayHow();
    }
}

最后,写一个main方法运行测试。

public class DecoratorTest {
    public static void main(String[] args) {
        DecoratorB cdb = new DecoratorB(new DecoratorA(new CoreComponent()));
        cdb.sayHello();
    }
}

其输出如下。

CoreComponent say Hello
DecoratorA say what are you doing?
DecoratorB say How are you?

2、代理模式

代理模式主要解决在直接访问对象时带来的问题,比如,要访问的对象在远程机器上。

在面向对象系统中,有些对象由于某些原因(对象创建开销大、某些操作需要安全控制、需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,所以我们可以在访问此对象时加上一个对此对象的访问层。

代理模式中有三个角色:

  • 核心能力接口

  • 委托类

  • 代理类

委托类和代理类都必须实现核心能力接口(即它们必须实现同一接口),委托类肯定是用于执行核心业务方法了,那代理类是干嘛的呢?

代理类可以做如下事情:

  1. 预处理消息

  2. 过滤消息

  3. 把消息转发给委托类

  4. 事后处理消息

代理模式常用的场景有:windows的快捷方式、spring aop等。熟悉spring框架的读者一定知道,在service层通过会打上声明式事务注解,声明式事务就是通过代理来实现的。

代理模式又分为两种:静态代理和动态代理。静态代理的代理类在程序运行时就创建好了,而动态代理的代理类是在程序运行时动态创建的。

下面以windows快捷方式的案例,分别介绍静态代理和动态代理。

2.1、静态代理

打开idea是一种能力,因此定义为接口,而idea64.exe和idea的快捷方式都具有这种能力,所以实现这个接口。idea64.exe是委托类,idea的快捷方式是代理类,我们一般是用快捷方式去打开idea的。

定义核心接口。

public interface OpenIdea {
    void open();
}

定义委托类。

public class Idea64Exe implements OpenIdea {
    @Override
    public void open() {
        System.out.println("通过idea64.exe打开idea");
    }
}

定义代理类。

public class IdeaShortcut implements OpenIdea {
    private Idea64Exe idea64Exe;

    @Override
    public void open() {
        if(null == idea64Exe){
            idea64Exe = new Idea64Exe();
        }
        System.out.println("通过idea快捷方式打开idea");
        idea64Exe.open();
    }
}

定义main方法运行测试。

public class StaticProxyTest {
    public static void main(String[] args) {
        IdeaShortcut proxy = new IdeaShortcut();
        proxy.open();
    }
}

程序的输出结果如下。

通过idea快捷方式打开idea
通过idea64.exe打开idea


2.2、动态代理

核心接口和委托类跟静态代理的一样,而动态代理的代理类由于是动态生成的,所以我们不能在程序运行前就写好代理类。

具体的做法是,写一个切面类(切面类不等同于代理类),委托类就好比切入点,在程序运行时,切面类会与委托类相结合(称为织入)从而生成一个代理类。

定义切面类。

public class OpenIdeaAspect implements InvocationHandler {

    private OpenIdea target;

    public OpenIdeaAspect(OpenIdea target) {
        this.target = target;
    }

    /**
     * 这个方法是给动态生成的代理类执行的
     * @param proxy 动态生成的额代理对象
     * @param method 要执行的方法
     * @param args 方法的参数
     * @return 目标对象
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是动态生成的代理类:"+proxy.getClass().getName());
        //定义代理类要做的事
        System.out.println("通过快捷方式打开idea");
        //返回目标对象要做的事
        return method.invoke(target);
    }
}

定义main方法运行测试。

public class DynamicProxyTest {
    public static void main(String[] args) {
        // 定义切面类
        OpenIdeaAspect aspect = new OpenIdeaAspect(new Idea64Exe());

        //在运行时动态生成一个代理类
        OpenIdea proxy = (OpenIdea)Proxy.newProxyInstance(DynamicProxyTest.class.getClassLoader(),
                new Class[]{OpenIdea.class},
                aspect);
        proxy.open();

    }
}

输出结果如下。

我是动态生成的代理类:com.sun.proxy.$Proxy0
通过快捷方式打开idea
通过idea64.exe打开idea

3、工厂方法模式

上篇文章 中讲了简单工厂模式,最后点出其不符合开闭原则,因此工厂方法模式就是冲着开闭原则来的。

简单工厂模式中的工厂可以生产所有动物,我们可以改造一下,将工厂定义为一个接口,不同的动物对应不同的工厂,而每一种工厂只能生成一种动物,最后由客户端决定使用哪种工厂。

说起来还挺好理解的吧,我就直接上代码了。

首先Animal、Cat、Dog、Pig类复用简单工厂模式中对应的类,详细代码请戳这里 设计模式之简单工厂+策略模式

定义AnimalFactory接口。

public interface AnimalFactory {
    Animal getAnimal();
}

定义CatFactory、DogFactory、PigFactory。

public class CatFactory implements AnimalFactory{
    @Override
    public Animal getAnimal() {
        return new Cat();
    }
}

public class DogFactory implements AnimalFactory{
    @Override
    public Animal getAnimal() {
        return new Dog();
    }
}

public class PigFactory implements AnimalFactory{
    @Override
    public Animal getAnimal() {
        return new Pig();
    }
}

定义main方法运行测试。

public class FacrotyMethodTest {
    public static void main(String[] args) {

        AnimalFactory factory = new DogFactory();
        Animal animal = factory.getAnimal();
        animal.sayHello();
    }
}

我的二维码

觉得写得不错的小伙伴,扫码关注一下我的公众号吧,谢谢呀!

猜你喜欢

转载自blog.csdn.net/xl_1803/article/details/112006902