策略模式、装饰器模式、代理模式

1 策略模式:定义一系列算法的方法,所有的算法功能相同但是实现不同。

  设计原则:
  1、封装变化
  2、多用组合,少用继承
  3、针对接口编程,不针对实现编程

示例类图:


image.png

如上类图所示:鸭子有两个可能的行为--飞行(FlyBehavior)和呱呱叫(QuackBehavior),为适应不
同的鸭子这两种行为的不同方式,这是会发生“变化”的部分,将它们封装起来,形成接口。然后
设计不同的策略类继承自行为接口,不同的鸭子都组合一个(FlyBehavior)和(QuackBehavior),以组合的方式形成一个具体鸭子。而在某个具体鸭子中,真正起作用的是
FlyBehavior和QuackBehavior的策略类即子类。
部分代码展示:

public abstract class Duck {

    protected FlyBehavior flyBehavior;

    protected QuackBehavior quackBehavior;

    public abstract void display();

    public void swim(){
        System.out.println("I can swim");
    }

    public void fly(){
        flyBehavior.fly();
    }

    public void quack(){
        quackBehavior.quack();
    }

    public void setFlyBehavior(FlyBehavior flyBehavior){
        this.flyBehavior = flyBehavior;
    }

    public void setQuackBehavior(QuackBehavior quackBehavior){
        this.quackBehavior = quackBehavior;
    }
}
public class MallardDuck extends Duck {

    public MallardDuck(){
        this.flyBehavior = new FlyWithWings();
        this.quackBehavior = new Quack();
    }

    @Override
    public void display(){
        System.out.println("MallardDuck display");
    }
}

2 装饰器模式:将核心功能与装饰器功能分开,以便动态添加功能。

  涉及的设计原则:
  1,对拓展开放,对修改关闭

示例类图:


image.png

如上类图所示,所有的类都是Beverage的子类,因此所有的类都可以用Beverage的引用来指
向,因此可以利用Beverage beverage = new Mocha(Beverage beverage)来进行反复为一杯咖
啡中添加mocha或者whip或者soy。
部分示例代码:

public abstract class Beverage {

    String description = "UnKnow Beverage";

    public String getDescription(){
        return description;
    }

    public abstract double cost();
}
public class DarkRoast extends Beverage{

    public DarkRoast(){
        description = "DarkRoast ";
    }

    @Override
    public double cost() {
        return 0.89;
    }
}
public abstract class CondimentDerator extends Beverage{

    public abstract String getDescription();
}
public class Mocha extends CondimentDerator{
    Beverage beverage;

    public Mocha(Beverage beverage){
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + " Mocha";
    }

    @Override
    public double cost() {
        return beverage.cost() + 0.15;
    }
}
public class Whip extends CondimentDerator{

    Beverage beverage;

    public Whip(Beverage beverage){
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + " Whip";
    }

    @Override
    public double cost() {
        return beverage.cost()+0.12;
    }
}
public class Test {
    public static void main(String[] args) {
        Beverage beverage1 = new DarkRoast();
        beverage1 = new Mocha(beverage1);
        beverage1 = new Mocha(beverage1);
        beverage1 = new Whip(beverage1);
        System.out.println("超优深焙咖啡,双份摩卡,打泡:" + beverage1.getDescription());
        System.out.println("cost:" + beverage1.cost() + "$");

        Beverage beverage2 = new HouseBlend();
        beverage2 = new Soy(beverage2);
        beverage2 = new Mocha(beverage2);
        beverage2 = new Whip(beverage2);
        System.out.println("首选咖啡:" + beverage2.getDescription());
        System.out.println("cost : " + beverage2.cost() + "$");
    }
}

3 代理模式

3.1 远程代理模式:利用javaRMI作代理,使另一个JVM上运行的对象可以在本地JVM上获取到。

图形示例:


图形来自header first 设计模式

客户端要获取服务端的某个对象,实际上是向RMI registry做请求,获取到服务器上的某个远程
对象,而这个远程对象又是服务器在编译的时候注册到RMI上的。因此,在客户端和服务端之间
就建立了一个以RMI registry做代理的通讯。
部分代码示例:
(1) 服务端将某个对象设置为远程对象,需要拓展(或继承)Remote接口---在java.rmi.*包中

public interface GumballMachineRemote extends Remote {

    int getCount()throws RemoteException;
    String getLocation() throws RemoteException;
    String getState() throws RemoteException;
}

(2)在服务端写远程对象的具体实现,,java.rmi.server.UnicastRemoteObject 所有可以被远程调用的对象都必须扩展该类

public class GumballMachine extends UnicastRemoteObject implements GumballMachineRemote {

    private String location;
    private int count;

    public GumballMachine(String location,int count) throws RemoteException{
        this.count = count;
        this.location = location;
    }

    @Override
    public int getCount() throws RemoteException {
        return count;
    }

    @Override
    public String getLocation() throws RemoteException {
        return location;
    }

    @Override
    public String getState() throws RemoteException {
        return "state";
    }
}

(3) 服务端将远程对象注册进入RMI registry中

public class GumballMachineTestDrive {

    public static void main(String[] args) {
        try {
            //启动RMI注册服务,指定端口为1099 (1099为默认端口)
            LocateRegistry.createRegistry(1099);
            //创建服务对象
            GumballMachineRemote gumballMachine = new GumballMachine("10.0.0.1",5);
            //把gumballMachine注册到RMI注册服务器上,命名为gumballMachine
            Naming.rebind("gumballMachine",gumballMachine);
        }catch (RemoteException | MalformedURLException e){
            e.printStackTrace();
        }
    }
}

(4) 书写客户端程序

public class GumballMonitor {

    GumballMachineRemote gumballMachineRemote;

    public GumballMonitor(GumballMachineRemote machineRemote){
        this.gumballMachineRemote = machineRemote;
    }

    public void report(){
        try{
            System.out.println("Gumball Machine:" + gumballMachineRemote.getLocation());
            System.out.println("Current inventory:" + gumballMachineRemote.getCount() + " gumballs");
            System.out.println("Current state:" + gumballMachineRemote.getState());
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

(5) 客户端调用测试:

public class GumballMonitorTestDrive {

    public static void main(String[] args) {
        try {
            GumballMachineRemote gumballMachineRemote = (GumballMachineRemote) Naming.lookup("rmi://127.0.0.1:1099/gumballMachine");
            GumballMonitor gumballMonitor = new GumballMonitor(gumballMachineRemote);
            gumballMonitor.report();
        } catch (NotBoundException | MalformedURLException | RemoteException e) {
            e.printStackTrace();
        }
    }
}

3.2 虚拟代理模式:对于一些占用系统资源较多或者加载时间较长的对象,可以给这些对象

提供一个虚拟代理。在真实对象创建成功之前虚拟代理扮演真实对象的替身,而当真实对象创建
之后,虚拟代理将用户的请求转发给真实对象。
图形示例:


图形来自header first 设计模式

如图所示:在客户端发出请求的时候,首先是代理进行处理,代理在开始创建对象的同时,会给
客户端提供一个返回值,而不是让客户端进行无意义地等待中,知道真实对象创建成功后,则会
将请求委托给真实对象。
图片处理的时候,用虚拟代码显示良好信息的代码示例:

public class ImageProxy implements Icon {

    private ImageIcon imageIcon;
    private URL imageURL;
    private Thread retrievalThread;
    private boolean retrieving = false;

    @Override
    public void paintIcon(final Component c, Graphics g, int x, int y) {
        if(imageIcon != null){
            imageIcon.paintIcon(c,g,x,y);
        }else {
            g.drawString("Loading image....",x+300,y+300);
            if(!retrieving){
                retrieving = true;
                retrievalThread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try{
                            imageIcon = new ImageIcon(imageURL,"CD Cover");
                            c.repaint();
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                });
                retrievalThread.start();
            }
        }
    }

    @Override
    public int getIconWidth() {
        if(imageIcon != null){
            return imageIcon.getIconWidth();
        }else {
            return 800;
        }
    }

    @Override
    public int getIconHeight() {
        if(imageIcon != null){
            return imageIcon.getIconHeight();
        }else {
            return 600;
        }
    }
}

在创建真实对象ImageIcon之前,会给客户端显示"Loading Image...”的提示信息

3.3 静态代理:静态代理是最简单的一种代理模式,在程序运行前,某个类的代理委托关系

已经确定好了。
类图示例:


图形来自header first 设计模式

代码示例:(出处:https://www.jianshu.com/p/a8aa6851e09e)

public interface Subject {  
     public void buyMac();
}
public class RealSubject implement Subject{
    @Override
    public void buyMac() {  
        System.out.println(”买一台Mac“);  
    }  
}
public class Proxy  implements Subject{
  
    @Override
    public void buyMac{
      
      //引用并创建真实对象实例,即”我“
      RealSubject realSubject = new RealSubject();

      //调用真实对象的方法,进行代理购买Mac
      realSubject.buyMac();
      //代理对象额外做的操作
      this.WrapMac();
    }

     public void WrapMac(){
      System.out.println(”用盒子包装好Mac“);  
    }
}
public class ProxyPattern {

    public static void main(String[] args){

    Subject proxy = new Proxy();
    proxy.buyMac();
    }
        
}

3.4 动态代理(保护代理):在程序运行时动态创建一个代理类,实现一个或多个接口,并

将方法的调用转发到指定的类中。
类图:


图形来自Header first 设计模式

创建动态代理类需要通过调用Proxy的静态方法newProxyInstance方法,即:

static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,                                        
                                                   InvocationHandler h)

ClassLoader loader:被代理类的类加载器(可以通过反射获取,如:person.getClass().getClassLoader())
Class<?>[] interfaces:被代理类要实现的接口列表(可以通过反射获取,如:person.getClass().getInterfaces())
InvocationHandler h:代理处理程序(即实现InvocationHandler接口的代理类)

创建一个代理类示例:

PersonBean personBean = (PersonBean)Proxy.newProxyInstance(person.getClass().getClassLoader,
                                                               person.getClass().getInterfaces(),
                                                               new OwnerInvocationHandler(person))

动态代理的类都需要继承InvocationHandler类,这个类中只有一个方法:

Object invoke(Object proxy, Method method, Object[] args)

Ojbect proxy:表示需要代理的对象
Method method:表示要操作的方法
Object[] args:method方法所需要传入的参数(可能没有为,null.也可能有多个)

代码示例:
建立被代理类接口和实现类

public interface PersonBean {

    String getName();
    String getGender();
    String getInterests();
    int getHotOrNotRating();

    void setName(String name);
    void setGender(String gender);
    void setInterests(String interests);
    void setHotOrNotRating(int rating);
}

class PersonBeanImpl implements PersonBean{

    private String name;
    private String gender;
    private String interests;
    private int rating;
    private int ratingCount = 0;

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getGender() {
        return gender;
    }

    @Override
    public String getInterests() {
        return interests;
    }

    @Override
    public int getHotOrNotRating() {
        if(ratingCount == 0) return 0;
        return (rating/ratingCount);
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public void setInterests(String interests) {
        this.interests = interests;
    }

建立动态代理类:

class NonOwnerInvocationHandler implements InvocationHandler {
    private PersonBean personBean;

    NonOwnerInvocationHandler(PersonBean personBean) {
        this.personBean = personBean;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if(method.getName().equals("setHotOrNotRating")) {
                return  method.invoke(personBean, args);
            } else  if(method.getName().startsWith("set")){
                throw new IllegalAccessException();
            }
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}

class OwnerInvocationHandler implements InvocationHandler {
    private PersonBean personBean;

    OwnerInvocationHandler(PersonBean personBean){
        this.personBean = personBean;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try{
            if(method.getName().startsWith("get")){
                return method.invoke(personBean,args);
            }else if(method.getName().equals("setHotOrNotRating")){
                throw new IllegalAccessException();
            }else if(method.getName().startsWith("set")){
                return method.invoke(personBean,args);
            }
        }catch (InvalidParameterException e){
            e.printStackTrace();
        }
        return null;
    }
}

测试类:

public class MatchMakingTestDrive {
    public static void main(String[] args) {
        PersonBean person = new PersonBeanImpl();
        person.setGender("male");
        person.setName("richard");
        person.setInterests("reading");
        person.setHotOrNotRating(5);
        System.out.println("has made personBean!");

        PersonBean ownerPerson = getOwnerProxy(person);
        try {
            ownerPerson.setHotOrNotRating(1);
        } catch (Exception e) {
            System.out.println("owner can't setHotOrNotRating");
        }
        ownerPerson.setInterests("science");

        PersonBean nonOwnerPerson = getNonOwnerProxy(person);
        try {
            nonOwnerPerson.setInterests("write");
        } catch (Exception e) {
            System.out.println("nonOwner can't setInterests");
        }
        nonOwnerPerson.setHotOrNotRating(10);

    }

    private static PersonBean getOwnerProxy(PersonBean person) {
        return (PersonBean) Proxy.newProxyInstance(person.getClass().getClassLoader(),
                person.getClass().getInterfaces(),
                new OwnerInvocationHandler(person));
    }

    private static PersonBean getNonOwnerProxy(PersonBean person) {
        return (PersonBean)Proxy.newProxyInstance(person.getClass().getClassLoader(),
                person.getClass().getInterfaces(),
                new NonOwnerInvocationHandler(person));
    }
}

3.5 cglib动态代理:对于有接口的类,可以用上述动态代理,当某个实现类没有实现接口时,就需要用cglib动态代理。

原文见:http://blog.csdn.net/yakoo5/article/details/9099133/
代码示例:
被代理类:

public class SayHello {  
   public void say(){  
   System.out.println("hello everyone");  
   }  
} 

该类实现了创建子类的方法与代理的方法。getProxy(SuperClass.class)方法通过入参即父类的
字节码,通过扩展父类的class来创建代理对象。intercept()方法拦截所有目标类方法的调用,obj
表示目标类的实例,method为目标类方法的反射对象,args为方法的动态入参,proxy为代理类
实例。proxy.invokeSuper(obj, args)通过代理类调用父类中的方法。

public class CglibProxy implements MethodInterceptor{  
 private Enhancer enhancer = new Enhancer();  
 public Object getProxy(Class clazz){  
  //设置需要创建子类的类  
  enhancer.setSuperclass(clazz);  
  enhancer.setCallback(this);  
  //通过字节码技术动态创建子类实例  
  return enhancer.create();  
 }  
 //实现MethodInterceptor接口方法  
 public Object intercept(Object obj, Method method, Object[] args,  
   MethodProxy proxy) throws Throwable {  
  System.out.println("前置代理");  
  //通过代理类调用父类中的方法  
  Object result = proxy.invokeSuper(obj, args);  
  System.out.println("后置代理");  
  return result;  
 }  
}  

测试:

public class DoCGLib {  
 public static void main(String[] args) {  
  CglibProxy proxy = new CglibProxy();  
  //通过生成子类的方式创建代理类  
  SayHello proxyImp = (SayHello)proxy.getProxy(SayHello.class);  
  proxyImp.say();  
 }  
}

结果:

前置代理  
hello everyone  
后置代理 

总结

策略模式是定义某个接口,以不同的实现类来代替实现某种功能,

这些实现类的功能是一样的,但是具体实现方式不同。

装饰器模式是定义不同的装饰类,这些装饰类都是核心类的子类,

同时核心类也是这些装饰类的一个属性,可以动态地为该核心类动态添加不同的功能模块(装饰类)

代理模式是将某个具体类包装起来,通过代理类来处理消费方和生产方之间的交互,

同时也可以在代理类中添加一些额外的操作。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,598评论 18 139
  • 1 场景问题# 1.1 访问多条数据## 考虑这样一个实际应用:要一次性访问多条数据。 这个功能的背景是这样的;在...
    七寸知架构阅读 2,969评论 1 52
  • Java 面试题 整理自牛客网 什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”? Java虚拟...
    GuaKin_Huang阅读 10,659评论 19 265
  • 目录 本文的结构如下: 引言 什么是代理模式 模式的结构 典型代码 代理模式分类 代码示例 代理模式和装饰者模式的...
    w1992wishes阅读 1,517评论 0 13
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,864评论 6 13