23种设计模式Java版第三篇

Posted 小二玩编程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了23种设计模式Java版第三篇相关的知识,希望对你有一定的参考价值。

ps:本文是转载文章,阅读原文可以获取源码,文章末尾有原文链接

ps:这一篇是写装饰者模式、代理模式和外观模式模式

1、装饰者模式

在不改变现有对象结构的情况下,用生成子类的方式动态地给该对象增加一些职责,装饰者与被装饰者拥有共同的父类;一般情况下,如果扩展一个类的功能会使用继承用子类来实现,增加扩展功能同时,子类也会变得很多;在类结构不变的前提下,如果使用组合方式来创建一个包装对象来包裹真实对象,为其提供额外的功能,就会变得灵活很多。

装饰者模式具有以下几种角色:

(1)抽象构件角色:定义一个接口以规范实现额外功能的子类。

(2)具体构件角色:实现抽象构件角色,添加一些额外功能。

(3)抽象装饰角色:实现抽象构件角色,它不一定是抽象类,它持有抽象构件角色实例,可以通过抽象装饰角色的子类扩展具体构件的功能。

(4)具体装饰角色:继承抽象装饰角色,重写抽象装饰角色已经重写的方法,并给具体构件附加责任。

下面用代码进行演示一下:

(1)抽象构件角色,定义一个 ICar 接口:

public interface ICar {

public void move();

}

(2)具体构件角色,新建一个 Car 类并实现 ICar:

public class Car implements ICar{

@Override
public void move() {

System.out.println("陆地上跑");

}

}

(3)抽象装饰角色,新建一个 SuperCar 类并实现 ICar 接口,同时内容拥有 ICar 类型的属性对象:

public class SuperCar implements ICar{

protected ICar car;

public SuperCar(ICar car) {

super();
this.car = car;

}

@Override
public void move() {

car.move();

}

}

(4)具体装饰角色,新建一个 WaterCar 类并继承SuperCar,同时附加责任方法 swim:

public class WaterCar extends SuperCar{

public WaterCar(ICar car) {

super(car);

}

public void swim() {
  System.out.println("水上游");
}
@Override
public void move() {
  super.move();
  swim();
}

}

(5)具体装饰角色,新建一个 FlyCar 类并继承 SuperCar,同时附加责任方法 fly:

public class FlyCar extends SuperCar{

public FlyCar(ICar car) {

super(car);

}
public void fly() {

System.out.println("天上飞");

}

@Override
public void move() {
  super.move();
  fly();
}

}

(6)在客户端进行调用测试:

Car car = new Car();
FlyCar flyCar = new FlyCar(car);
WaterCar waterCar = new WaterCar(flyCar);
waterCar.move();

日志打印如下所示:

图片

首先是 FlyCar 装饰 Car,WaterCar 装饰 FlyCar,然后再调用 WaterCar 的 move方法,最终执行顺序Car.move()->FlyCar.fly()->WaterCar.swim(),也就是说装饰在最里面的类,它的方法最先调用。

SuperCar 类持有 ICar 接口,方法全部委托给 ICar 接口调用,它其实是想交给 ICar 接口的实现类(Car、FlyCar 和 WaterCar)进行调用;SuperCar 抽象装饰角色的子类(SuperCar、SuperCar),里面的构造方法都调用 super(car) ,这里就反应了抽象依赖于实现的原则;原因是构造方法里面参数都是 ICar 接口,只要把 ICar 的实现类传递进去,所以就能够做到一层一层的被装饰;当调用到 WaterCar.move() 方法时,就会先调用 FlyCar.move() 方法,而 FlyCar.move() 方法就会先调用 Car.move()方法,原因是 FlyCar 类装饰 Car 类,WaterCar 类装饰 FlyCar 类,FlyCar 和 WaterCar 在构造方法中调用 super 方法传递 ICar 接口并指向了具体一个装饰者类,这个装饰者模式在 Java 的 I/O 流中就有用到。

2、代理模式

给某对象提供一个代理以控制对该对象的访问,这时候访问对象和目标对象没有直接的关联,代理对象这时候就成为了访问对象和目标对象之间关联的中介。

代理模式包含以下几种角色

(1)抽象主题类:声明真实主题和代理对象必须要实现的功能方法一个接口或者抽象类。

(2)真实主题类:实现了抽象主题类的功能业务,是代理对象本质上要引用的对象。

(3)代理类:实现了抽象主题类的功能业务,其方法内调用真实主题类对象的方法,本质上没有扩展抽象主题类的功能。

代理模式可分为静态代理和动态代理

2、1 静态代理

静态代理,由开发者创建代理类对其编译,在程序执行前代理类和目标类在代码中是确定的,对目标功能进行扩展时,静态代理要在不修改目标功能的条件下才可以进行,否则没有意义。

下面对静态代理模式用代码进行一下演示

(1)抽象主题类,新建一个 Star 接口:

public interface Star {

public void confer();
public void signContract();
public void bookTicket();
public void sign();
public void collectMoney();

}

(2)真实主题类,新建一个 RealStar 并实现 Star 接口:

public class RealStar implements Star{

@Override
public void confer() {

System.out.println("RealStar confer");

}

@Override
public void signContract() {

    System.out.println("RealStar signContract");    

}

@Override
public void bookTicket() {

System.out.println("RealStar bookTicket");

}

@Override
public void sign() {

System.out.println("周杰伦本人唱歌");

}

@Override
public void collectMoney() {

System.out.println("RealStar collectMoney");

}

}

(3)代理类,新建一个 ProxyStar 类并实现 Star 接口:

public class ProxyStar implements Star{

private Star realStar;
public ProxyStar(Star realStar) {
  this.realStar = realStar;
}

@Override
public void confer() {

System.out.println("ProxyStar confer");

}

@Override
public void signContract() {

System.out.println("ProxyStar signContract");

}

@Override
public void bookTicket() {

System.out.println("ProxyStar bookTicket");

}

@Override
public void sign() {

realStar.sign();

}

@Override
public void collectMoney() {

System.out.println("ProxyStar collectMoney");

}

}

(4)在客户端进行测试调用:

    Star realStar = new RealStar();
    ProxyStar proxyStar = new ProxyStar(realStar);
    proxyStar.confer();
    proxyStar.bookTicket();
    proxyStar.signContract();
    proxyStar.sign();
    proxyStar.collectMoney();

日志打印如下所示:

图片

2、2 动态代理

动态代理,程序在执行的过程中,通过反射机制动态创建代理对象,能够通过代理对象动态地增加调用的方法。

下面对动态代理模式用代码进行一下演示

(1)代理类,在上面静态代理模式代码的基础上不变,然后新建一个 StarHandler 类并实现 InvocationHandler 接口:

public class StarHandler implements InvocationHandler {
Star realStar;

public StarHandler(Star realStar) {

this.realStar = realStar;

}

public void confer() {

System.out.println("ProxyStar confer");

}

public void signContract() {

System.out.println("ProxyStar signContract");

}

public void bookTicket() {

System.out.println("ProxyStar bookTicket");

}

public void collectMoney() {

System.out.println("ProxyStar collectMoney");

}

@Override
public Object invoke(Object proxy, Method method, Object[] args)

  throws Throwable {
Object result = null;
confer();
signContract();
bookTicket();
if (method.getName().equals("sign")) {
result = method.invoke(realStar, args);
}
collectMoney();
return result;

}

}

(2)在客户端进行测试调用:

    Star star = new RealStar();
    StarHandler starHandler = new StarHandler(star);
    Star proxy = (Star)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Star.class}, starHandler);
    proxy.sign();

日志打印如下所示:

图片

上面2个代理模式的案例中,使用了java的多态;静态代理模式中真实主题类和代理类都实现了 Star 接口,但大多数的实现都交给代理类,比如 confer、signContract、bookTicket 和 collectMoney 方法,只有真实主题类实现 sign 方法,那些不重要的活就交给代理类来处理,重要的活就交给真实主题类来处理;动态代理模式中,代理类实现了 InvocationHandler 接口,可在 invoke(Object proxy, Method method, Object[] args) 方法中动态调用真实主题类的方法。

代理类和真实主题类都实现了同一个接口,使得代理类能拓展真实主题类的功能的功能;代理模式能将客户端与真实主题类分离,在一定程度上增加了程序的可扩展性;代理模式在一定程度上造成系统设计中类的数量增加,在实际运用中推荐使用动态代理,因为动态代理的底层不用我们自己去实现,可以使用 Java 内部提供的 API,动态创建代理对象,动态调用真实主题类对象的方法。

3、外观模式

外观模式是一种为多个子系统提供同一个的接口,从而使这些子系统更加容易被调用,也使得这些子系统更好的被管理,外观角色是将子系统“分而治之”;该模式又称为门面模式,对外有一个统一方法,客户端就不用关心内部子系统的实现细节。

外观模式有如下角色:

(1)外观角色:为多个子系统对外提供一个公共的接口,用于访问子系统。

(2)子系统角色:实现子系统的功能,提供给外观角色访问。

下面我们举个例子,假设一个学生去入学,学校是一个系统,那么入学的过程会经过“出示报到证,填写个人资料”、“领取宿舍号、床号、以及宿舍钥匙”和“缴纳学费”这几个步骤;学生要以这几个流程打交道,就好像客户端和几个子系统打交道是很累,这时候我们引入外观模式,学校设置一个接待员,学生只接触接待员,而接待员就帮学生办理入学的流程,这样就轻松了很多;我们用代码模拟一下。

(1)子系统角色,新建一个 ChildSystem1 类:

public class ChildSystem1 {

public void handle() {
  System.out.println("出示报到证,填写个人资料");
}

}
(2)子系统角色,新建一个 ChildSystem2 类:

public class ChildSystem2 {
public void handle() {

  System.out.println("领取宿舍号、床号、以及宿舍钥匙");
}

}

(3)子系统角色,新建一个 ChildSystem3类:

public class ChildSystem3 {
public void handle() {

  System.out.println("缴纳学费");
}

}

(4)外观角色,新建一个 Facade 类:

public class Facade {

private ChildSystem1 mChildSystem1 = new ChildSystem1();
private ChildSystem2 mChildSystem2 = new ChildSystem2();
private ChildSystem3 mChildSystem3 = new ChildSystem3();
public void handle() {
  mChildSystem1.handle();
  mChildSystem2.handle();
  mChildSystem3.handle();
}

}

(5)在客户端进行测试调用:

Facade facade = new Facade();
facade.handle();

日志打印如下所示:

图片

客户端只需要跟 Facade 类交互,而 Facade 类只需要和 ChildSystem1、ChildSystem2、ChildSystem3 这3个子系统交互,Facade 类就好比如这3个子系统的“客户端”,使用 Facade 类,真正的客户端就不需要直接调用 ChildSystem1、ChildSystem2、ChildSystem3 这3个子系统,客户端也不需要关心 ChildSystem1、ChildSystem2、ChildSystem3 这3个子系统的实现细节;客户端和子系统之间任何一个改变互不影响,使得客户端和子系统的耦合性很低;但是在一定程度上不能限制客户端使用子系统类型,很有可能出现其他的问题。

以上是关于23种设计模式Java版第三篇的主要内容,如果未能解决你的问题,请参考以下文章

23种设计模式Java版第一篇

23种设计模式Java版第六篇

23种设计模式Java版第七篇

23种设计模式Java版第八篇

Java-第三篇3种创建的线程方式

设计模式就该这么学:以微信订阅号来讲观察者模式(第三篇)