装饰者模式&外观模式&代理模式

装饰者模式(Decorator) Component:组件(主体) concreteComponent:被装饰者 Decorator:装饰者 注意:concreteComponent、Decorator都会实现或继承Component角色 Client 类:装饰模式的调用者 Component:主体===》饮料(Drink) concreteComponent:被装饰者===》咖啡(Coffee) xxxConcreteCompo
摘要由CSDN通过智能技术生成

装饰者模式(Decorator)

  • Component:组件(主体)

  • concreteComponent:被装饰者

  • Decorator:装饰者

注意:concreteComponent、Decorator都会实现或继承Component

角色

  • Client 类:装饰模式的调用者

  • Component:主体===》饮料(Drink)

  • concreteComponent:被装饰者===》咖啡(Coffee)

  • xxxConcreteComponent:具体的被装饰者(ChinaCoffee)美式咖啡、欧式咖啡、中式咖啡

  • Decorator:装饰器===》调料

  • xxxDecorator:子装饰器糖类、奶酪、牛油、芥末  

案例

单体咖啡与调味组合的饮料计价项目

-  使用前

这个项目最容易想到的设计方案就是采用继承的设计方案,设计思路如下:

​ Drink===》饮品

​ Juice===》果汁

​ .......

​ Coffee===》咖啡

​ ChinaCoffee===》中式咖啡

​ ASeasoningChinaCoffee===》被A调料修饰的中式咖啡

​ BSeasoningChinaCoffee===》被B调料修饰的中式咖啡

​ ......

​ XxxCoffee===》其它咖啡

​ ASeasoningXxxCoffee===》被A调料修饰的其它咖啡

​ BSeasoningXxxCoffee===》被B调料修饰的其它咖啡

​ .....

上面每个类中都有getPrice计价的功能,从结果上来看,确实可以完成项目需求,但是整个设计体系过于臃肿,不便于后期的扩展与维护;

①.Drink类

/**
 * 饮料包括单体咖啡+调料
 */
public abstract class Drink {
    protected double price;
    protected int n;
    protected DrinkSeasoning seasoning;

    public abstract double getPrice();
}

/**
 * 单体咖啡
 */
abstract class Coffee extends Drink {
}

/**
 * 单体果汁
 */
abstract class Juice extends Drink {
}

class ChinaCoffee extends Coffee{
    ChinaCoffee(double price,int n){
        this.price = price;
        this.n = n;
    }

    @Override
    public double getPrice() {
        return this.price*this.n+this.seasoning.getPrice();
    }
}

②.DrinkSeasoning类

/**
 * 调料的抽象接口
 */
public interface DrinkSeasoning {
    public abstract double getPrice();
}

/**
 * A类调料
 */
class ADrinkSeasoning implements  DrinkSeasoning{
    protected double price;
    protected int n;
    ADrinkSeasoning(double price,int n){
        this.price = price;
        this.n = n;
    }
    @Override
    public double getPrice() {
        return this.price*this.n;
    }
}


/**
 * B类调料
 */
class BDrinkSeasoning implements  DrinkSeasoning{
    private double price;
    protected int n;
    BDrinkSeasoning(double price,int n){
        this.price = price;
        this.n = n;
    }
    @Override
    public double getPrice() {
        return this.price*this.n;
    }
}

③.Client类

public class Client {
    public static void main(String[] args) {
        ChinaCoffee chinaCoffee = new ChinaCoffee(6,1);
        ADrinkSeasoning aDrinkSeasoning = new ADrinkSeasoning(2,2);
        chinaCoffee.seasoning = aDrinkSeasoning;
        System.out.println("中式咖啡1份+A调料2份,最终价格为:"+chinaCoffee.getPrice());

//        思考1:如果我要下单中式咖啡1份+A调料3份+B调料2份,计算出最终的价格,那代码该怎么改动呢?
            seasoning只能代表一种调料

//        思考2:在原有的咖啡订单下,追加B调料2份,计算出最终的价格,那代码该怎么改动呢?
            原有的设计没办法应对这种场景
    }
}

执行结果为:

 

-  使用后

①.Drink类

public abstract class Drink {
    protected double price;
    protected int n;

    public abstract double getPrice();
}

/**
 * 单体咖啡
 */
abstract class Coffee extends Drink {
}

/**
 * 单体果汁
 */
abstract class Juice extends Drink {
}

class ChinaCoffee extends Coffee {
    ChinaCoffee(double price, int n) {
        this.price = price;
        this.n = n;
    }

    @Override
    public double getPrice() {
        return this.price * this.n;
    }
}

②.DecoratorDrink类

public class DecoratorDrink extends Drink {
    private Drink drink;

    public DecoratorDrink(Drink drink, double price, int n) {
        this.drink = drink;
        this.price = price;
        this.n = n;
    }

    @Override
    public double getPrice() {
        return this.price * this.n + drink.getPrice();
    }
}

class ADecoratorDrink extends DecoratorDrink {
    public ADecoratorDrink(Drink drink, double price, int n) {
        super(drink, price, n);
    }
}

class BDecoratorDrink extends DecoratorDrink {
    public BDecoratorDrink(Drink drink, double price, int n) {
        super(drink, price, n);
    }
}

③.Client类

public class Client {
    public static void main(String[] args) {
        ChinaCoffee chinaCoffee = new ChinaCoffee(6,1);
//        假定A类调料2元一份,B类调料3元一份
        Drink order = new ADecoratorDrink(chinaCoffee, 2, 2);
        System.out.println("中式咖啡1份+A调料2份,最终价格为:"+order.getPrice());

//        思考1:如果我要下单中式咖啡1份+A调料3份+B调料2份,计算出最终的价格,那代码该怎么改动呢?
        order = new ADecoratorDrink(order,2,1);
        System.out.println("式咖啡1份+A调料3份,最终价格为:"+order.getPrice());
        order = new BDecoratorDrink(order,3,2);
        System.out.println("式咖啡1份+A调料3份+B调料2份,最终价格为:"+order.getPrice());

//        思考2:在原有的咖啡订单下,追加B调料2份,计算出最终的价格,那代码该怎么改动呢?
        order = new BDecoratorDrink(order,3,2);
        System.out.println("式咖啡1份+A调料3份+B调料4份,最终价格为:"+order.getPrice());
    }
}

执行结果为:

注意事项及细节

  • 装饰者模式一般用于对原有功能进行增强/装饰

  • 动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性

应用

  • IO流体系:缓冲流

外观模式(Facade)

  • 术语

          Facade:外观

  • 角色

    Client 类
          外观模式的调用者
      ComponentA
          子系统A
              电脑
      ComponentB
          子系统B
              投影仪
      Facade
          HomeTheaterFacade 
              家庭影院

案例

需求:组装一个家庭影院;

电脑打开、投影仪打开、音箱打开、灯光调暗、零食拿出来,电影开始;

零食收起来、灯光调亮、音箱关闭、投影仪关闭、电脑关闭,电影结束;

使用前

/**
 * 电脑(故意写两个用不上的功能,依次体现外观模式的优点)
 */
public class ComponentA {
    public void m1(){
        System.out.println("电脑功能一...");
    }
    public void m2(){
        System.out.println("电脑功能二...");
    }

    public void on(){
        System.out.println("电脑打开...");
    }

    public void off(){
        System.out.println("电脑关闭...");
    }
}

//投影仪
class ComponentB {
    public void on(){
        System.out.println("投影仪打开...");
    }

    public void off(){
        System.out.println("投影仪关闭...");
    }
}

//音箱
class ComponentC {
    public void on(){
        System.out.println("音箱打开...");
    }

    public void off(){
        System.out.println("音箱关闭...");
    }
}

//、灯光
class ComponentD {
    public void on(){
        System.out.println("灯光调亮...");
    }

    public void off(){
        System.out.println("灯光调暗...");
    }
}

//零食
class ComponenE {
    public void on(){
        System.out.println("零食拿出来...");
    }

    public void off(){
        System.out.println("零食收起来...");
    }
}
public class Client {
    public static void main(String[] args) {
        new ComponentA().on();
        new ComponentB().on();
        new ComponentC().on();
        new ComponentD().off();
        new ComponenE().on();
        System.out.println("电影开始了...");

        System.out.println();
        new ComponenE().off();
        new ComponentD().on();
        new ComponentC().off();
        new ComponentB().off();
        new ComponentA().off();
        System.out.println("电影结束了...");
    }
}

从上面代码可以看出:

客户端调用依赖了所有的子系统(ABCDE),如果该需求反复出现,对于客户端调用而言,就不是很方便了;

另一方面,此需求完成只需要依赖各个子系统的其中一部分功能,其它功能客户端用不上,依照迪米特法则我们也应该解耦客户端与各个子系统的关系;

使用后

public class ComponentA {
    public void m1(){
        System.out.println("电脑功能一...");
    }
    public void m2(){
        System.out.println("电脑功能二...");
    }

    public void on(){
        System.out.println("电脑打开...");
    }

    public void off(){
        System.out.println("电脑关闭...");
    }
}

//投影仪
class ComponentB {
    public void on(){
        System.out.println("投影仪打开...");
    }

    public void off(){
        System.out.println("投影仪关闭...");
    }
}

//音箱
class ComponentC {
    public void on(){
        System.out.println("音箱打开...");
    }

    public void off(){
        System.out.println("音箱关闭...");
    }
}

//、灯光
class ComponentD {
    public void on(){
        System.out.println("灯光调亮...");
    }

    public void off(){
        System.out.println("灯光调暗...");
    }
}

//零食
class ComponentE {
    public void on(){
        System.out.println("零食拿出来...");
    }

    public void off(){
        System.out.println("零食收起来...");
    }
}
public class ComponentFacade {
    ComponentA componentA =new ComponentA();
    ComponentB componentB = new ComponentB();
    ComponentC componentC = new ComponentC();
    ComponentD componentD = new ComponentD();
    ComponentE componenE = new ComponentE();
    public void on(){
        componentA.on();
        componentB.on();
        componentC.on();
        componentD.off();
        componenE.on();
        System.out.println("电影开始了...");
    }

    public void off(){
        componenE.off();
        componentD.on();
        componentC.off();
        componentB.off();
        componentA.off();
        System.out.println("电影结束了...");
    }
}
public class Client {
    public static void main(String[] args) {
        ComponentFacade componentFacade = new ComponentFacade();
        componentFacade.on();
        System.out.println();
        componentFacade.off();
    }
}

从上面可以看出,客户端只依赖了外观类,彻底解耦了与各个子系统之间的关系;

  • 注意事项及细节

    • 屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性

    • 对客户端与子系统的耦合关系 - 解耦,让子系统内部的模块更易维护和扩展

    • 当系统需要进行分层设计时,可以考虑使用 Facade 模式

  • 应用

    后期代码维护,提取某些类中可用的方法聚合在一起

代理模式(Proxy)

  • 分类

       --  静态代理  

            角色
                接口
                   ITeacherDao
                目标对象
                   TeacherDAO 
                代理类
                   TeacherDAOProxy 
         细节
             代理对象与目标对象要实现相同的接口
             调用的时候通过调用代理对象的方法来调用目标对象

①.TeacherDAO类

public class TeacherDAO implements ITeacherDao {
    public void teach() {
        System.out.println("老师传授知识");
    }
}

//目标接口
interface ITeacherDao {
    void teach();
}

//代理类
class TeacherDAOProxy implements ITeacherDao {
    private ITeacherDao teacherDAO;

    public TeacherDAOProxy(ITeacherDao teacherDAO) {
        this.teacherDAO = teacherDAO;
    }

    @Override
    public void teach() {
        System.out.println("老师正式授课前的准备工作,如学生全部签到...");
        teacherDAO.teach();
        System.out.println("老师结束授课,如下课铃声响起...");
    }
}

②.Client类

public class Client {
    public static void main(String[] args) {
        TeacherDAOProxy proxy = new TeacherDAOProxy(new TeacherDAO());
        proxy.teach();
    }
}

- 动态代理

    - ​    jdk代理
      ​        角色
      ​            接口
      ​                ITeacherDao
      ​            目标对象
      ​                TeacherDAO 
      ​            代理类
      ​                TeacherDAOProxy 
      ​        细节
      ​            不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
      ​            代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象
      ​            代理类所在包:java.lang.reflect.Proxy

①.ITeacherDao类

/**
 * 目标接口
 */
interface ITeacherDao {
    String teach();

    ITeacherDao sleep(int minutes);
}

class TeacherDao implements ITeacherDao{
    @Override
    public String teach() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.format(new Date())+":老师传授知识";
    }

    @Override
    public ITeacherDao sleep(int minutes) {
        System.out.println("老师睡了" + minutes + "分钟");
        return this;
    }

}

//真实代理类的外衣
class TeacherDaoProxy{
    private ITeacherDao target;

    public TeacherDaoProxy(ITeacherDao target) {
        this.target = target;
    }

    public Object xxx(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object obj = null;
                        String methodName = method.getName();
                        System.out.println("目标方法" + methodName + ":jdk代理开始...");
                        System.out.println("真实代理对象:"+proxy.getClass());
                        System.out.println("目标对象:"+target.getClass());
                        if("sleep".equals(methodName)){
//                            method.invoke(target, args);
//                            obj = proxy;
                            obj = method.invoke(target, args);
                        }else {
//                        proxy是真实代理类,method是目标方法,args是目标方法携带的参数
                            obj = method.invoke(target, args);
                        }
                        System.out.println("目标方法" + methodName + ":jdk代理结束...");
                        return obj;
                    }
                });
    }
}

②.Client类

public class Client {
    public static void main(String[] args) {
        TeacherDaoProxy proxy = new TeacherDaoProxy(new TeacherDao());
        ITeacherDao ins = (ITeacherDao) proxy.xxx();
        System.out.println("===========代理类实例被使用   begin=============");
        System.out.println(ins);
        System.out.println("===========代理类实例被使用   end=============");
        System.out.println(ins.teach());
//        System.out.println(proxy.execute());
        System.out.println("===========代理类实例被使用   begin=============");
        ins.sleep(10);
        System.out.println("===========代理类实例被使用   end=============");
        ins.sleep(20).sleep(60);
    }
}

 执行结果为:

Cglib代理
      ​        角色
      ​            接口
      ​                ITeacherDao
      ​            目标对象
      ​                TeacherDAO 
      ​            代理类
      ​                TeacherDAOProxy 

细节
      ​            目标对象与代理对象都不需要实现接口
      ​            Cglib 代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展
      ​            Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口.它广泛的被许多 AOP 的框架使用
      ​            Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类

注意:
      1)需要引入 cglib 的 jar 文件
      2)在内存中动态构建子类,注意代理的类不能为 final,否则报错java.lang.IllegalArgumentException:
      3)目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;


class TeacherDao {
    public String teach() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.format(new Date()) + ":老师传授知识";
    }

    public TeacherDao sleep(int minutes) {
        System.out.println("老师睡了" + minutes + "分钟");
        return this;
    }

}

//真实代理类的外衣
class TeacherDaoProxy implements MethodInterceptor {
    private Object target;

    public TeacherDaoProxy(Object target) {
        this.target = target;
    }

    //返回一个代理对象:	是 target  对象的代理对象
    public Object getProxyInstance() {
        //1. 创建一个工具类
        Enhancer enhancer = new Enhancer();
        //2. 设置父类
        enhancer.setSuperclass(target.getClass());
        //3. 设置回调函数
        enhancer.setCallback(this);
        //4. 创建子类对象,即代理对象
        return enhancer.create();
    }

    /**
     * @param proxyIns  由CGLib动态生成的代理类实例
     * @param method    上文中实体类所调用的被代理的方法引用
     * @param args      参数值列表
     * @param methodProxy   生成的代理类对方法的代理引用
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object proxyIns, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        String methodName = method.getName();
        Object res;
        System.out.println("目标方法" + methodName + ":cglib代理开始...");
        System.out.println("真实代理对象:" + proxyIns.getClass());
        System.out.println("目标对象:" + target.getClass());
        if ("sleep".equals(methodName)) {
//                            method.invoke(target, args);
//                            obj = proxy;
            res = method.invoke(target, args);
//            res = methodProxy.invokeSuper(proxyIns,args);
            res = proxyIns;
        } else {
//                        proxy是真实代理类,method是目标方法,args是目标方法携带的参数
            res = method.invoke(target, args);
        }
        System.out.println("目标方法" + methodName + ":cglib代理结束...");
        return res;
    }
}
public class Client {
    public static void main(String[] args) {
        TeacherDao proxy = (TeacherDao) new TeacherDaoProxy(new TeacherDao()).getProxyInstance();
        proxy.sleep(111).sleep(222);
    }
}

最后执行结果为:

至此,装饰者模式&外观模式&代理模式介绍完毕,由于作者水平有限难免有疏漏,欢迎留言纠错。 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值