引言
原文链接:带你读懂几种常见的设计模式 第二弹 希望点进去的小伙伴关注一下我的公众号哟,文末有二维码,谢谢!
今天的文章继续介绍设计模式。上一弹中介绍了简单工厂模式和策略模式,本文将介绍装饰器模式、代理模式和工厂方法模式。
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、代理模式
代理模式主要解决在直接访问对象时带来的问题,比如,要访问的对象在远程机器上。
在面向对象系统中,有些对象由于某些原因(对象创建开销大、某些操作需要安全控制、需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,所以我们可以在访问此对象时加上一个对此对象的访问层。
代理模式中有三个角色:
-
核心能力接口
-
委托类
-
代理类
委托类和代理类都必须实现核心能力接口(即它们必须实现同一接口),委托类肯定是用于执行核心业务方法了,那代理类是干嘛的呢?
代理类可以做如下事情:
-
预处理消息
-
过滤消息
-
把消息转发给委托类
-
事后处理消息
代理模式常用的场景有: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();
}
}
我的二维码
觉得写得不错的小伙伴,扫码关注一下我的公众号吧,谢谢呀!