精华内容
下载资源
问答
  • Java设计模式

    千次阅读 多人点赞 2019-09-03 23:20:31
    Java设计模式 1.工厂模式 工厂模式一般分为简单工厂、工厂、抽象工厂3钟情况,属于创建型设计...5.代理模式 6.状态模式 7.访问者模式 8.命令模式 9.装饰器模式 10.组合模式 ...

    Java设计模式

    1.工厂模式

    2.生成器模式

    3.观察者模式

    4.桥接模式

    5.代理模式

    6.状态模式

    7.访问者模式

    8.命令模式

    9.装饰器模式

    10.组合模式

    1.工厂模式

    工厂模式一般分为简单工厂、工厂、抽象工厂3种情况,属于创建型设计模式。

    简单工厂(静态工厂)

    //定义小汽车接口:ICar.java
    public interface ICar{
      //由于工厂模式仅关系对象的创建,为说明方便,无需定义方法
    }
    
    //下面定义高、中、低档具体的汽车
    //高档小汽车:TopCar.java
    public class TopCar implements ICar{
    }
    //中档小汽车:MidCar.java
    public class MidCar implements ICar {
    }
    //低档小汽车:LowCar.java
    public class LowCar implements ICar {
    }
    
    //简单工厂:CarSimpleFactory.java
    public class CarSimpleFactory {
        public static final String TOPTYPE = "toptype";
        public static final String MIDTYPE = "midtype";
        public static final String LOWTYPE = "lowtype";
        public static ICar create(String mark){
        ICar obj = null;
        if(mark.equals(TOPTYPE)){ //如果是高档类型
          obj = new TopCar();  //则创建高档车对象
        }
        else if(mark.equals(MIDTYPE)){
        obj = new MidCar();
      }
        else if(mark.equals(LOWTYPE)){
        obj = new LowCar();
      }
        return obj;    //返回选择的对象
      }
    }
    
    //测试程序:CarTest.java
    public class CarTest {
      public static void main(String[] args) {
      //从工厂中创建对象
        ICar obj = CarSimpleFactory.create("toptype");
      }
    }
    

    工厂

    //定义小汽车接口:ICar.java
    public interface ICar {
    //由于工厂模式仅关系对象的创建,为说明方便,无需定义方法
    }
    //定义高、中、低档具体的小汽车
    //高档小汽车:TopCar.java
    public class TopCar implements ICar {
    }
    //中档小汽车:MidCar.java
    public class MidCar implements ICar {
    }
    //低档小汽车:LowCar.java
    public class LowCar implements ICar {
    }
    //定义抽象工厂:AbstractFactory.java
    public abstract class AbstractFactory {
    public abstract ICar create();
    }
    //定义高档小汽车工厂:TopFactory.java
    public class TopFactory extends AbstractFactory {
    public ICar create() {
        return new TopCar(); //高档工厂生成高档小汽车对象
      }
    }
    //定义中档小汽车工厂:MidFactory.java
    public class MidFactory extends AbstractFactory {
    public ICar create() {
        return new MidCar(); //中档工厂生成中档小汽车对象
      }
    }
    //定义低档小汽车工厂:LowFactory.java
    public class LowFactory extends AbstractFactory {
    public ICar create() {
        return new LowCar(); //低档工厂生成低档小汽车对象
      }
    }
    //测试类:CarTest.java
    public class CarTest {
    public static void main(String []args){
        AbstractFactory obj = new TopFactory();//多态创建高档工厂
        ICar car = obj.create();    //获得高档工厂中的小汽车对象
      }
    }
    

    抽象工厂

    //小汽车接口
    public interface ICar { }
    public class TopCar implements ICar { }
    public class MidCar implements ICar { }
    public class LowCar implements ICar { }
    
    //定义公共汽车接口、高、中、低档公共汽车类
    public interface IBus { }
    public class UpBus implements IBus { }
    public class MidBus implements IBus { }
    public class DnBus implements IBus { }
    
    //定义抽象工厂:AbstractFactory.java
    public absttract class AbstractFactory {
    public abstract ICar createCar(); //产生小汽车对象
    public abstract IBus createBus(); //产生公共汽车对象
    }
    
    //定义高档工厂:TopFactory.java
    public class TopFactory extends AbstractFactory {
      public ICar createCar() {
        return new TopCar();  //高档工厂生成高档小汽车对象
      }
      public IBus createBus() {
        return new UpBus();  //高档工厂生成高档公共汽车对象
      }
    }
    
    //定义中档工厂:MidFactory.java
    public class MidFactory extends AbstractFactory {
      public ICar createCar() {
        return new MidCar();  //中档工厂生成中档小汽车对象
      }
      public IBus createBus() {
        return new MidBus();  //中档工厂生成中档公共汽车对象
      }
    }
    //定义低档工厂:LowFactory.java
    public class LowFactory extends AbstractFactory {
      public ICar createCar() {
        return new LowCar();  //低档工厂生成中档小汽车对象
      }
      public IBus createBus() {
        return new DnBus();  //低档工厂生成中档公共汽车对象
      }
    }
    

    2.生成器模式

    生成器模式也称为建造者模式。生成器模式的意图在于将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。在软件设计中,有时候面临着一个非常复杂的对象的创建工作。这个复杂的对象通常可以分成几个较小的部分,由各个子对象组合出这个复杂对象的过程相对来说比较稳定,但是子对象的创建过程各不相同并且可能面临变化。根据OOD中的OCP原则,应该对这些子对象的创建过程进行变化封装。

    生成器思路是产品类与创建产品的类相分离。产品类仅1个,创建产品的类有n个。

    生成器设计模式涉及4个关键角色:产品(Product)、抽象生成器(IBuild)、具体生成器(Builder)、指挥者(Director)。
    在这里插入图片描述

    (1)定义1个产品类。
    public class Unit1{……}
    public class Unit2{……}
    public class Unit3{……}
    public class Product {
    Unit1 u1;
    Unit2 u2;
    Unit3 u3;
    }
    由于不在该类完成Product类对象的创建,所以无需显示定义构造方法。
    
    (2)定义n个生成器Build类。
    根据语义,生成器是用来生成Product对象的,因此一般来说,Product是生成器类的一个成员变量;
    根据语义,每创建一个Product对象,本质上都需要先创建Unit1,Unit2,…, UnitN,再把它们组合成所需的Product对象,
    因此需要n个createUnit()方法及一个组合方法composite();由于createUnit()及composite()是共性,
    因此可定义共同的生成器类接口, n个生成器类均从此接口派生即可。代码如下。
    //定义生成器类接口IBuild
    public interface IBuild {
    public void createUnit1();
    public void createUnit2();
    public void createUnit3();
    public Product composite();    //返回值是Product对象
    }
    //定义3个生成器类
    public class BuildProduct implements IBuild { //生成第一种Product
    Product p = new Product();     //Product是成员变量
    public void createUnit1() {
    //p.u1= ...       //创建Unit1
    }
    public void createUnit2() {
    //p.u2 = ...       //创建Unit2
    }
    public void createUnit3() {
    //p.u3 =  //创建Unit3
    }
    public Product composite() {
    //...   //关联Unit,Unit2,Unit3
    return p;  //返回Product对象p
    }
    }
    public class BuildProduct2 implements IBuild {  //生成第2种Product
    Product p = new Product();//Product是成员变量
    public void createUnit() {/*p.u = ...... */} //创建Unit
    public void createUnit2(){/*p.u2 = ...... */} //创建Unit2
    public void createUnit3(){/*p.u3 = ...... */} //创建Unit3
    public Product composite() {
    //......    //关联Unit1,Unit2,Unit3
    return p;   //返回Product对象p
    }
    }
    public class BuildProduct3 implements IBuild {  //生成第3种Product
    Product p = new Product();//Product是成员变量
    public void createUnit1() {/*p.u1 = ...... */} //创建Unit1
    public void createUnit2(){/*p.u2 = ...... */} //创建Unit2
    public void createUnit3(){/*p.u3 = ...... */} //创建Unit3
    public Product composite() {
    //......  //关联Unit1,Unit2,Unit3
    return p;  //返回Product对象p
    }
    }
    通过上述代码可知,若需求分析发生变化,只需增加或删除相应的生成器类即可,无需修改已有的类代码。
    (3)定义1个统一调度类,也叫指挥者(Director)类,是对生成器接口IBuild的封装。该类及简单的测试代码如下。
    public class Director {
    private IBuild build;
    public Director(IBuild build){
    this.build = build;
    }
    public Product build(){
    build.createUnit1();
    build.createUnit2();
    build.createUnit3();
    return build.composite();
    }
    public static void main(String []args){
    IBuild build = new BuildProduct();
    Director direct = new Director(build);
    Product p = direct.build();
    }
    }
    

    3.观察者模式

    观察者设计模式适合解决多种对象跟踪一个对象数据变化的程序结构问题,有一个称作“主题”的对象和若干个称作“观察者”的对象。有一个主题数据——温度,3个观察者—温度日志、温度曲线、温度报警。因此观察者设计模式涉及两种角色:主题和观察者。
    观察者设计模式可以从以下递推中得出一些重要结论。
    ● 主题要知道有哪些观察者对其进行监测,因此主题类中一定有一个集合类成员变量,包含了观察者的对象集合。
    ● 既然包含了观察者的对象集合,那么,观察者一定是多态的,有共同的父类接口。
    ● 主题完成的主要功能是:可以添加观察者,可以撤销观察者,可以向观察者发消息,引起观察者响应。这三个功能是固定的,因此主题类可以从固定的接口派生。
    因此,编制观察者设计模式,要完成以下功能类的编制。
    ● 主题ISubject接口定义。
    ● 主题类编制。
    ● 观察者接口IObserver定义。
    ● 观察者类实现。
    在这里插入图片描述

    (1)观察者接口IObserver。
    
        public interface IObserver {
            public void refresh(String data);
        }
        
    (2)主题接口ISubject。
    
        public interface ISubject {
            public void register(IObserver obs);       //注册观察者
            public void unregister(IObserver obs);     //撤销观察者
            public void notifyObservers();             //通知所有观察者
        }
        
    (3)主题实现类Subject。
    
        public class Subject implements ISubject {
            private Vector<IObserver> vec = new Vector();  //观察者维护向量
            private String data;                           //主题中心数据
    
            public String getData() {
                return data;
            }
            public void setData(String data) {              //主题注册(添加)
                this.data = data;
            }
            public void register(IObserver obs) {           //主题注册(添加)观察者
             vec.add(obs);
            }
    
            public void unregister(IObserver obs) {         //主题撤销(删除)观察者
                if(vec.contains(obs))
                    vec.remove(obs);
            }
            public void notifyObservers(){             //主题通知所有观察者进行数据响应
                for(int i=0; i<vec.size(); i++){
                    IObserver obs = vec.get(i);
                    obs.refresh(data);
                }
            }
        }
        
        主题实现类Subject是观察者设计模式中最重要的一个类,包含了观察者对象的维护向量vec以及主题中心数据data变量与具体观察者对象的关联
        方法(通过nitofyObservers())。也就是说,从此类出发,可以更深刻地理解ISubject为什么定义了3个方法、IObserver接口为什么定义了1个方法。
        
    (4)一个具体观察者类Observer。
    
        public class Observer implements IObserver {
            public void refresh(String data) {
                System.out.println("I have received the data:" +data);
            }
        }
        
    (5)一个简单的测试类Test。
    
        public class Test {
            public static void main(String[] args) {
                IObserver obs = new Observer();    //定义观察者对象
                Subject subject = new Subject();
                //定义主题对象
                subject.register(obs);             //主题添加观察者
                subject.setData("hello");          //主题中心数据发生变动
                subject.notifyObservers();         //通知所有观察者进行数据响应
            }
        }
        
    该段代码的含义是:当主题中心数据变化(通过setData方法)后,主题类subject要调用notifyObservers()方法,
    通知所有观察者对象接收数据并进行数据响应。
    

    4.桥接模式

    桥接模式是关于怎样将抽象部分与它的实现部分分离,使它们都可以独立地变化的成熟模式。
    在这里插入图片描述

    (1)定义邮寄接口IPost。
    
        public interface IPost{   //邮局
            public void post();   //发送功能
        }
        
    (2)两个具体邮寄类SimplePost、MarkPost。
    
        //平信邮寄类SimplePost
        class SimplePost implements IPost{     //平信
            public void post(){                //发送
                System.out.println("This is Simple post");
            }
        }
        //挂号邮寄类
        class MarkPost implements IPost{       //挂号
            public void post(){                //发送
                System.out.println("This is Mark post");
            }
        }
        
      经过(1)、(2)的论述,完成了语义的前半部分定义:邮局有发送功能;发送有两种方式,平邮和挂号。
      
    (3)抽象事物类AbstractThing。
    
        abstract class AbstractThing{ //抽象事物
    
            private IPost obj;         //有抽象发送功能
            public AbstractThing(IPost obj){
                this.obj = obj;
            }
            public void post(){
                obj.post();
            }
        }
        
    该类是桥接模式的核心。分析语义“信件和包裹共享平邮与挂号功能”:信件、包裹是两个不同的事物,它们有共享的功能,
    也一定有相异的功能。共享的功能一定能封装到一个类中,又由于该类不能代表一个具体的事物,所以把它定义成abstract类是恰当的。
    该类共享的是多态成员obj,是IPost类型的,是抽象的、泛指的,用一条语句表明了事物共享平邮和发送功能。
    
    (4)具体事物类Letter、Parcel。
    
        //信件类Letter
        class Letter extends AbstractThing{
            public Letter(IPost obj){
                super(obj);
            }
            //其他独有变量和方法
        }
        //包裹类Parcel
        class Parcel extends AbstractThing{
            public Parcel(IPost obj){
                super(obj);
            }
            //其他独有变量和方法
        }
        
        //编制一个简单的测试类
        public class Test {
            public static void main(String[] args) {
                IPost p = new SimplePost();
    
                Letter letter = new Letter(p);
                letter.post();
            }
        }
        
    第一种情况:若增加了新的事物,则只需从Abstract派生一个类即可,其他无需改变。
    
        class NewThing extends AbstractThing{
            public NewThing(IPost obj){
                super(obj);
            }
            //其他独有变量和方法
        }
        
    第二种情况:若增加了新的邮寄类别,比如特快专递,则只需从IPost接口派生一个类即可,其他无需改变。
    
        class EMSPost implements IPost{        //特快专递
        public void post(){                //发送
                System.out.println("This is EMS post");
            }
        }
    

    5.代理模式

    代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不能看到的内容和服务或者添加客户需要的额外服务。代理模式则是一种可以很好实现客户对象与代理对象分离的策略。

    代理模式的定义如下:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式的英文叫作Proxy或Surrogate,它是一种对象结构型模式。其抽象UML图如图所示
    在这里插入图片描述
    代理模式包含如下角色
    ● ISubject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口。
    ● RealSubject:真实主题角色,是实现抽象主题接口的类。
    ● Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。以买电视为例,其代码如下。
    (1)定义抽象主题——买电视。

    interface ITV{
            public void buyTV();
        }
    

    (2)定义实际主题——买电视过程。

    class Buyer implements ITV{
        public void buyTV(){
            System.out.println("I have bought the TV by buyer proxy");
        }
    }
    

    真正的付费是由购买者完成的。
    (3)定义代理。

    class BuyerProxy implements ITV{
        private Buyer buyer;
        public BuyerProxy(Buyer buyer){
            this.buyer = buyer;
        }
        public void buyTV(){
            preProcess();
            buyer.buyTV();
            postProcess();
        }
        public void preProcess(){
            //询问客户需要电视类型、价位等信息
        }
        public void postProcess(){
            //负责把电视送到客户家
        }
    }
    

    电视代理商BuyerProxy与购买者Buyer都实现了相同的接口ITV,是对Buyer对象的进一步封装。着重理解buyTV()方法:首先代理商要通过preProcess()询问客户买电视的类型、价位等信息,然后购买者通过buyer.buyTV()自己付费完成电视购买,最后代理商通过postProcess()协商具体的送货服务、产品三包等。

    代理模式最突出的特点是:代理角色与实际主题角色有相同的父类接口。常用的代理方式有4类:虚拟代理、远程代理、计数代理、动态代理

    虚拟代理
    虚拟代理的关键思想是:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。当用户请求一个“大”对象时,虚拟代理在该对象真正被创建出来之前扮演着替身的角色;当该对象被创建出来之后,虚拟代理就将用户的请求直接委托给该对象。

    远程代理
    远程代理的含义是:为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以在同一台主机中,也可在另一台主机中。也就是说,远程对象驻留于服务器上,客户机请求调用远程对象调用相应方法,执行完毕后,结果由服务器返回给客户端。
    在这里插入图片描述
    计数代理
    当客户程序需要在调用服务提供者对象的方法之前或之后执行日志或计数的额外功能时,就可以使用计数代理模式。计数代理模式并不是把这些额外操作的代码直接添加到源服务中,而是把它们封装成一个单独的对象,这就是计数代理。

    动态代理
    对代理模式而言,一个主题类与一个代理类一一对应,这也是静态代理模式的特点。
    静态代理模式简图
    但是,也常存在这样的情况,有n个主题类,但是代理类中的“前处理、后处理”都是一样的,仅调用主题不同,需要编制如图所示的程序框架。
    [动态代理模式简图]
    也就是说,多个主题类对应一个代理类,共享“前处理、后处理”功能,动态调用所需主题,大大减小了程序规模,这就是动态代理模式的特点。实现动态代理的关键技术是反射。

    6.状态模式

    状态模式为研究各种状态以及状态间相互转化的实现方式提出了一种较好的设计思路。

    ● 状态类有共同的父接口(或抽象类), n个不同的状态实现类。
    ● 事物类中包含状态类父接口成员变量声明,用以反映语义“事物有n个状态”。
    ● 事物类中一定有方法选择分支,判断事物当前处于何种状态。

    状态模式必须完成如下内容的编制:
    ● State:状态接口,封装特定状态所对应的行为。
    ● ConcreteState:具体实现状态处理的类。
    ● Context:事物类,也称上下文类,通常用来定义多态状态接口,同时维护一个用来具体处理当前状态的示例对象。

    状态模式的UML抽象类图如图所示
    在这里插入图片描述
    状态模式的具体抽象代码如下。
    (1)定义状态抽象接口IState。

    interface IState{
        public void goState();
    }
    

    (2)定义状态实现类。

    class ConcreteStateA implements IState{//定义状态A类
        public void goState(){
            System.out.println("This is ConcreteStateA");
        }
    }
    class ConcreteStateB implements IState{//定义状态B类
        public void goState(){
            System.out.println("This is ConcreteStateB");
        }
    }
    

    (3)定义状态上下文维护类Context。

    class Context{            //上下文有n种状态
        private IState state;
        public void setState(IState state){
            this.state = state;
        }
        public void manage(){
            //此处代码根据条件选择某种状态
            state.goState(); //执行某种状态功能
        }
    }
    

    Context类是实现状态模式的关键,本部分仅列出了状态模式的基本代码

    7.访问者模式

    访问者模式的目的是封装一些施加于某种数据结构元素之上的操作,一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。为不同类型的元素提供多种访问操作方式,且可以在不修改原有系统的情况下增加新的操作方式,这就是访问者模式的模式动机。

    访问者模式抽象类图
    在这里插入图片描述
    访问者模式主要涉及以下四种角色:
    ● IElement:抽象的事物元素功能接口,定义了固定功能方法及可变功能方法接口。
    ● Element:具体功能的实现类。
    ● IVisitor:访问者接口,为所有访问者对象声明一个visit方法,用来代表为对象结构添加的功能,原则上可以代表任意的功能。
    ● Visitor:具体访问者实现类,实现要真正被添加到对象结构中的功能。

    考虑这样一个应用:已知三点坐标,编写功能类,求该三角形的面积和周长。

    如果采用访问者模式,应当这样思考:目前已确定的需求分析是求面积和周长功能,但有可能将来求三角形的重心、垂心坐标,内切、外界圆的半径等,因此,在设计时必须考虑如何屏蔽这些不确定情况。具体代码如下。

    1.定义抽象需求分析接口IShape

    interface IShape{
        float getArea();             //明确的需求分析
        float getLength();           //明确的需求分析
        Object accept(IVisitor v);   //可扩展的需求分析
    }
    

    着重理解可扩展的需求分析方法accept(),它在形式上仅是一个方法,但是按访问者模式而言,它却可以表示将来可以求重心、垂心坐标等功能,是一对多的关系,因此IVisitor一般来说是接口或抽象类,“多”项功能一定是由IVisitor的子类来实现的。那么为什么返回值是Object类型呢?可以这样理解,例如重心坐标由两个浮点数表示,外接圆半径由一个浮点数表示,为了屏蔽返回值差异,返回值定义成Object,表明可以返回任意对象类型。

    2.定义具体功能实现类Triangle

    class Triangle implements IShape{
        float x1, y1, x2, y2, x3, y3;           //三角形三点坐标
        public Triangle(float x1, float y1, float x2, float y2, float x3, float y3){
    
            this.x1=x1; this.y1=y1;
            this.x2=x2; this.y2=y2;
            this.x3=x3; this.y3=y3;
        }
        public float getDist(float u1, float v1, float u2, float v2){   //求任意两点距离
            return (float)Math.sqrt((u1-u2)*(u1-u2)+(v1-v2)*(v1-v2));
        }
        public float getArea(){            //固定需求分析求面积
            float a = getDist(x1, y1, x2, y2);
            float b = getDist(x1, y1, x3, y3);
            float c = getDist(x2, y2, x3, y3);
            float s = (a+b+c)/2;
            return (float)Math.sqrt(s*(s-a)*(s-b)*(s-c)); //海伦公式求面积
        }
        public float getLength(){          //固定需求分析求周长
            float a = getDist(x1, y1, x2, y2);
            float b = getDist(x1, y1, x3, y3);
            float c = getDist(x2, y2, x3, y3);
            return a+b+c;
        }
        public Object accept(IVisitor v){  //可扩展需求分析
            return v.visit(this);
        }
    }
    

    着重理解accept()方法,可以看出IVisitor接口中一定定义了多态方法visit(),那为什么把this引用传过去呢?可以这样理解:例如求三角形重心坐标,它的功能一定是在IVisitor的子类实现的,那么该子类一定得知道三角形的三个顶点坐标,因此把this引用传过去,相当于IVisitor的子类可访问Triangle类的成员变量,编制求重心坐标就容易了。

    3.定义访问者接口IVisitor

    interface IVisitor{
        Object visit(Triangle t);
    }
    

    至此为止,有了1、2、3的代码,访问者模式的代码框架就已经构建起来了。如果需求分析没有变化,那么程序一直应用即可;如果需求分析发生变化,则基础功能类不用变化,只要定义IVisitor接口的具体功能实现类就可以了,例如求三角形重心坐标代码如下。
    4.定义重心坐标实现类CenterVisitor

    class Point{
        float x, y;
    }
    class CenterVisitor implements IVisitor{
        public Object visit(Triangle t){
            Point pt = new Point();
            pt.x = (t.x1+t.x2+t.x3)/3;
            pt.y = (t.y1+t.y2+t.y3)/3;
            return pt;
        }
    }
    

    一个简单的测试类如下。

    public class Test3 {
        public static void main(String[] args) {
            IVisitor v = new CenterVisitor();        //定义求重心具体访问者对象
            Triangle t = new Triangle(0,0,2,0,0,2);  //定义三角形对象
            Point pt = (Point)t.accept(v);           //通过访问者对象求三角形重心坐标
            System.out.println(pt.x+"\t"+pt.y);
        }
    }
    

    可以知道,如果再想增加一个求三角形外接圆半径功能,只需再定义一个新类实现IVisitor接口,在该类中完成求外接圆半径功能即可。

    8.命令模式

    顾名思义,命令模式一定是有命令发送者、命令接收者。命令发送者负责发送命令,命令接收者负责接收命令并完成具体的工作。

    命令模式主要针对需要执行的任务或用户提出的请求进行封装与抽象。抽象的命令接口描述了任务或请求的共同特征,而实现则交由不同的具体命令对象完成。每个命令对象都是独立的,它负责完成需要执行的任务,却并不关心是谁调用它。

    命令模式抽象UML类图
    在这里插入图片描述
    命令模式一般有4种角色,如下所示。
    ● ICommander:抽象命令者,是一个接口,规定了用来封装请求的若干个方法。
    ● ConcreteCommander:具体命令发送者,即命令源。它是实现命令接口的类的示例,如上文中的Teacher类。
    ● Invoker:请求者,具体命令的管理与维护类。请求者是一个包含“命令接口”变量的类的示例。请求者中的“命令”接口的变量可以存放任何具体命令的引用,请求者负责调用具体命令,让具体命令执行那些封装了请求的方法。
    ● Receiver:命令接收者,是一个类的示例。该示例负责执行与请求相关的操作,如上文中的Student类。

    考虑老师通知学生打扫卫生的程序描述,具体代码如下。
    1.抽象命令接口ICommand

    interface ICommand{
        public void sweep();
    }
    

    2.命令接收者Student

    class Student{
        public void sweeping(){
            System.out.println("we are sweeping the floor");
        }
    }
    

    在命令模式中,具体工作一定是在接收者中完成的,这一点非常重要。示例中“清扫”工作是由sweeping()方法完成的。
    3.命令发送者Teacher

    class Teacher implements ICommand{
        private Student receiver = null;
        public Teacher(Student receiver){
            this.receiver = receiver;
        }
        public void sweep(){  //发送sweep清扫命令
            receiver.sweeping();
        }
    }
    

    命令发送者类中,一般来说包含命令接收者的引用,表明发送命令的目的地址。所以Teacher类中定义了接收者Student类对象的引用。而实现的抽象接口方法中表明发送命令的具体过程,sweep()中利用方法转发说明具体的清扫工作是由接收者Student对象完成的。
    4.命令请求者类Invoke

    class Invoke{
        ICommand command;
        public Invoke(ICommand command){
            this.command = command;
        }
        public void execute(){
            command.sweep();  //启动命令
        }
    }    
    

    在这里插入图片描述
    在这里插入图片描述
    普通思路是命令发送者直接作用命令接收者,而命令模式思路是在两者之间增加一个请求者类,命令发送者与请求者作用,请求者再与命令接收者作用,请求者起到了一个桥梁的作用

    5.一个简单的测试类

    public class Test {
        public static void main(String[] args)
        {
            Student s = new Student();         //定义接收者
            Teacher t = new Teacher(s);        //定义命令发送者
            Invoke invoke = new Invoke(t);     //将命令请求加到请求者对象中
            invoke.execute();                  //由请求者发送命令
        }
    }
    

    9.装饰器模式

    装饰器模式利用包含代替继承,动态地给一个对象添加一些额外的功能。以消息日志功能为例,其装饰器模式UML类图如图所示
    在这里插入图片描述
    装饰器模式主要有如下4种角色。
    ● 抽象构件角色(Component):
    它是一个接口,封装了将要实现的方法,如ILogger。
    ● 具体构件角色(ConcreteComponent):
    它是多个类,该类实现了Component接口,如FileLogger、ConsoleLogger。
    ● 装饰角色(Decorator):
    它是一个抽象类,该类也实现了Component接口,同时也必须持有接口Component的对象的引用,如事例中Decorator。
    ● 具体的装饰角色(Decorator类的子类,可以有一个,也可以有多个):
    这些类继承了类Decorator,实现了Component接口,描述了具体的装饰过程,如UpLogger、XMLLogger。

    1.抽象装饰器基类Decorator

    abstract class Decorator implements ILogger{
        protected ILogger logger;
        public Decorator(ILogger logger){
            this.logger = logger;
        }
    }
    

    2.具体装饰类

    //信息大写装饰类UpLogger
    class UpLogger extends Decorator{
        public UpLogger(ILogger logger){
            super(logger);
        }
        public void log(String msg) {
            msg = msg.toUpperCase();     //对字符串进行大写装饰
            logger.log(msg);             //再执行已有的日志功能
        }
    }
    
        //XML格式化装饰类XMLLogger
        class XMLLogger extends Decorator{
              public XMLLogger(ILogger logger){
                  super(logger);
              }
              public void log(String msg) {
                  String s = "<msg>\r\n" +
                           "<content>"+msg+"</content>\r\n"+
                            "<time>" + new Date().toString()+ "</time>\r\n"+
                            "</msg>\r\n";
                  logger.log(s);
             }
         }
    

    3.一个简单的测试类

    public class Test {
        public static void main(String[] args)throws Exception {
            ILogger existobj = new FileLogger();     //已有的日志功能
            ILogger newobj= new XMLLogger(existobj); //新的日志装饰类,对existobj装饰
            String s[] = {"how", "are", "you"};        //仿真传送的字符串信息数组
            for(int i=0; i<s.length; i++){
                newobj.log(s[i]);
                Thread.sleep(1000);                  //每隔1 s传送一个新的字符串
            }
            System.out.println("End");
        }
    }
    

    10.组合模式

    文件树型结构示例图
    在这里插入图片描述
    根目录是由两个子目录组成的,第一级子目录由两个文件组成,第二级子目录由两个文件组成,因此树型形式也可以称作组合形式。把叶子节点与目录节点都看成相同性质的节点,只不过目录节点后继节点不为空,而叶子节点后继节点为null。这样就能够对树型结构的所有节点执行相同的操作,这也是组合模式的最大特点。采用组合模式实现文件树型结构的功能,具体代码如下。

    1.定义抽象节点类Node

    abstract class Node{
        protected String name;
        public Node(String name){
            this.name = name;
        }
        public void addNode(Node node)throws Exception{
            throw new Exception("Invalid exception");
        }
        abstract void display();
    }
    

    该类是叶子节点与目录节点的父类,节点名称是name。其主要包括两类方法:一类方法是所有节点具有相同形式、不同内容的方法,这类方法要定义成抽象方法,如display();另一类方法是目录节点必须重写,叶子节点无需重写的方法,相当于为叶子节点提供了默认实现,如addNode()方法,因为叶子对象没有该功能,所以可以通过抛出异常防止叶子节点无效调用该方法。

    2.文件叶子节点类FileNode

    class FileNode extends Node{
        public FileNode(String name){
            super(name);
        }
        public void display(){
            System.out.println(name);
        }
    }
    

    该类是Node的派生类,仅重写display()方法即可。

    3.目录节点类DirectNode

    class DirectNode extends Node{
        ArrayList<Node> nodeList = new ArrayList();
        public DirectNode(String name){
            super(name);
        }
        public void addNode(Node node)throws Exception{
            nodeList.add(node);
        }
        public void display(){
    
            System.out.println(name);
            for(int i=0; i<nodeList.size(); i++){
                nodeList.get(i).display();
            }
        }
    }
    

    该类从Node抽象类派生后,与原DirectNode类相比,主要有以下不同:①由定义两个集合类成员变量转为定义一个集合类成员变量nodeList; ②由定义两个添加方法转为定义一个添加方法addNode(); ③display()方法中,由两个不同元素的循环转为一个对相同性质节点Node的循环。也就是说,原DirectNode中不论是定义成员变量、成员方法,还是方法内部的功能,都要实时考虑叶子节点、目录节点的不同性,因此它的各种定义一定是双份的。而组合模式中认为叶子节点、目录节点是同一性质的节点,因此与原DirectNode类对比,它的各种定义工作一定是减半的,也易于扩充。

    4.一个简单的测试类

    public class Test {
        public static void createTree(Node node)throws Exception{
            File f = new File(node.name);
            File f2[] = f.listFiles();
            for(int i=0; i<f2.length; i++){
                if(f2[i].isFile()){
                    Node node2 = new FileNode(f2[i].getAbsolutePath());
                    node.addNode(node2);
                }
                if(f2[i].isDirectory()){
                    Node node2 = new DirectNode(f2[i].getAbsolutePath());
                    node.addNode(node2);
                    createTree(node2);
                }
            }
        }
        public static void main(String[] args)throws Exception {
            Node start = new DirectNode("d://data");
            createTree(start);
            start.display();
        }
    }
    

    通过该示例,可得组合模式更一般的UML类图,如图所示。共包括以下三种角色。
    在这里插入图片描述
    ● 抽象节点:Node,是一个抽象类(或接口),定义了个体对象和组合对象需要实现的关于操作其子节点的方法,如add()、remove()、display()等。
    ● 叶节点:Leaf,从抽象节点Node派生,由于本身无后继节点,其add()等方法利用Node抽象类中相应的默认实现即可,只需实现与自身相关的remove()、display()等方法即可。
    ● 组合节点:Component,从抽象节点Node派生,包含其他Composite节点或Leaf节点的引用。
    总之,若某应用可形成树型结构,而且形成树型结构后可对叶节点及中间节点进行统一的操作,那么采用组合模式构建应用功能是一个比较好的选择。

    资料来自《Java设计模式深入研究》

    展开全文
  • 代理商说明现在回馈新老用户,决定开放代理授权。每个人都可以成为代理,目前代理分为4个等级,每个等级折扣不同, 初始为青铜代理。折扣是指从官方店铺拿货的折扣价,举例:白银代理为8折,现在官...

    代理商说明

    现在回馈新老用户,决定开放代理授权。每个人都可以成为代理,目前代理分为4个等级,每个等级折扣不同, 初始为青铜代理。
    折扣是指从官方店铺拿货的折扣价,举例:白银代理为8折,现在官方店铺有一件商品是100块,然后代理商自己标价200,卖出这件商品后,官方得到80块,代理商得到120块。

    简单来说,折扣就是代理商从官方的拿货价,至于卖多少,由代理商自己决定(最低价为官方价,最高价为官方价的5倍)。代理商不需要垫付一分钱,卖出商品后,钱会按照比例进入各自的账户。

    代理等级折扣销售额
    青铜代理10折销售额 < 400
    白银代理8折400 <= 销售额 < 1000
    黄金代理7折1000 <= 销售额 < 5000
    钻石代理5折销售额 >= 5000

    销售额以官方店铺价格为准,达到相应额度后,联系我给你升代理等级。比如官方店铺物品A价格100,那么代理商卖掉4件A就可以升级为白银代理。

    成为代理后,即便是自己使用,成为白银代理后,就可以8折购买,也是很划算的。

    如何成为代理

    1. 使用本链接 http://m.kuaifaka.com/invitied?code=77000846 注册账户,填写相关信息。

    2. 或者扫描二维码进入注册页面

    3. 账号注册成功后,输入邀请代理码,成为代理商,代理码 KFKA0EC279BE7D54

    4. 成为代理商后,就可以添加商品,然后重新标价,就有了一个独立的店铺。

      买家从代理商店铺购买物品,钱就会自动进入代理商账户。

    这和淘宝无货源卖货差不多,淘宝还需要交押金,快发卡不需要缴纳一分钱就可以卖货,稳赚不赔。

    展开全文
  • 大秀直播系统源码全套,后台总计+扣量系统+三级分销+分成CPS模式+代理后台权限【亲测运营版】 大秀直播完整站源码:后台总计+扣量系统+代理后台权限+三级分销分成 本源码可以打包APP没有PC版,主要以手机端为主【亲...
  • 大秀直播完整站源码:后台总计+扣量系统+代理后台权限+三级分销分成 本源码可以打包APP没有PC版,主要以手机端为主【亲测运营版,...大秀直播源码全套+带后台总计+带扣量系统+带代理后台权限+三级分销+带分成CPS模式
  • 设计模式代理模式

    千次阅读 2020-02-26 22:35:41
    代理模式是指为其他对象提供一种代理,以控制对这个对象的访问 特点:代理对象在客户端和目标对象之间起到中介的作用 代理模式(英语:Proxy Pattern)是程序设计中的一种设计模式。 所谓的代理者是指一个类别可以...

    前言

    代理模式是指为其他对象提供一种代理,以控制对这个对象的访问
    特点:代理对象在客户端和目标对象之间起到中介的作用

    代理模式(英语:Proxy Pattern)是程序设计中的一种设计模式。
    所谓的代理者是指一个类别可以作为其它东西的接口。代理者可以作任何东西的接口:网络连接、存储器中的大对象、文件或其它昂贵或无法复制的资源。

    了解代理模式的同学都知道,根据代理类生成的方式,可以分为静态代理动态代理静态代理是指开发者手动为目标类创建一个代理类,动态代理是指根据一系列规则,由JDK或者第三方类库动态生成代理类。

    静态代理

    静态代理相对动态代理稍微简单一些,因为代理类完完全全由开发者来编写。假设一个租房场景,用静态代理来实现
    首先有一个目标类就是房东

    /**
     * 房东
     * 目标类
     * @author sicimike
     * @create 2020-02-26 19:35
     */
    public class Landlord {
    
        public void signContract() {
            System.out.println("房东签合同...");
        }
    }
    

    房东只需要签合同就行了(核心服务),而其余非核心服务都由代理来完成。
    代理类就是中介或者二房东

    /**
     * 房东代理(中介、二房东)
     * 代理类
     * @author sicimike
     * @create 2020-02-26 19:37
     */
    public class LandlordProxy {
    
        private Landlord landlord = new Landlord();
    
        public void rentHouse() {
            System.out.println("草拟合同...");
            landlord.signContract();
            System.out.println("查水表...");
        }
    }
    

    代理类的作用就是把房子租出去,包括草拟合同,查水表等等,核心服务签合同还是由房东来完成。但是中介不会让你见到房东,也就是代理类实现了对目标类的访问控制。

    测试代码

    /**
     * @author sicimike
     * @create 2020-02-26 19:40
     */
    public class StaticProxyDemo {
    
        public static void main(String[] args) {
            LandlordProxy landlordProxy = new LandlordProxy();
            landlordProxy.rentHouse();
        }
    }
    

    执行结果

    草拟合同...
    房东签合同...
    查水表...
    

    这样租户在不接触房东的情况下,通过代理实现了租房的需求。

    静态代理就是开发者需要为每个目标类创建一个代理类。这样的话导致工作量剧增,并且类的数量过多,不易维护,于是便产生了动态代理

    动态代理

    动态代理是指程序在运行的过程中,根据一系列的规则,为目标类动态生成代理类。根据其实现方式不同,动态代理可以分成JDK动态代理Cglib动态代理

    JDK动态代理

    JDK是通过反射来创建代理类的,相关的类和接口主要有两个InvocationHandler接口和Proxy类,先看实例
    首先创建目标接口及其实现类,因为JDK动态代理是针对接口的,所以目标类(被代理的类)必须实现接口

    /**
     * 租赁服务
     * @author sicimike
     * @create 2020-02-26 20:08
     */
    public interface RentService {
    
        public void signContract();
    }
    
    /**
     * @author sicimike
     * @create 2020-02-26 20:09
     */
    public class RentServiceImpl implements RentService {
        @Override
        public void signContract() {
            System.out.println("签署合同....");
        }
    }
    

    再创建实现动态代理的关键类,用来采集日志

    /**
     * 日志处理器
     * @author sicimike
     * @create 2020-02-26 20:04
     */
    public class LogHandlerProxy implements InvocationHandler {
    
        // 目标对象
        private Object target;
    
        public LogHandlerProxy(Object target) {
            this.target = target;
        }
    
        public Object bind() {
            Class aClass = target.getClass();
            return Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(), this);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            befor();
            Object result = method.invoke(target, args);
            after();
            return result;
        }
    
        public void befor() {
            System.out.println("日志采集...");
        }
    
        public void after() {
            System.out.println("执行成功...");
        }
    }
    

    需要关注的几点是:

    • 动态代理的实现需要实现InvocationHandler接口,接口中只有一个方法invoke。方法中三个参数分别表示:代理对象、要执行的方法、方法参数
    • 由于动态代理所代理的类是不确定的,所以targetObject类型
    • 代理对象的产生是通过Proxy#newProxyInstance方法,三个参数分别表示:加载目标类的类加载器、目标类中所有接口、实现了InvocationHandler接口的类的实例

    测试代码

    /**
     * @author sicimike
     * @create 2020-02-26 20:10
     */
    public class JdkDynamicProxyDemo {
    
        public static void main(String[] args) {
            // RentService必须是接口
            RentService proxy = (RentService) new LogHandlerProxy(new RentServiceImpl()).bind();
            proxy.signContract();
        }
    }
    

    执行结果

    日志采集...
    签署合同....
    执行成功...
    

    所以JDK动态代理是靠反射来实现的,核心类就是InvocationHandlerProxy

    Cglib动态代理

    Cglib是利用ASM(字节码增强)技术来实现动态代理,也就是直接修改字节码。根据目标类生成一个子类作为代理类,所以目标类以及被代理的方法均不能被final修饰
    使用Cglib需要引入相关的jar包

    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.4</version>
    </dependency>
    

    Cglib方式实现动态代理主要涉及到一个接口net.sf.cglib.proxy.MethodInterceptor和一个类net.sf.cglib.proxy.Enhancer。先来看个实例
    首先创建目标类

    /**
     * 被代理的类和方法均不能被final修饰
     * @author sicimike
     * @create 2020-02-26 21:13
     */
    public class RentServiceImpl {
    
        public void signContract() {
            System.out.println("签署合同....");
        }
    }
    

    再创建实现动态代理的关键类,用来采集日志

    /**
     * 日志处理器
     * @author sicimike
     * @create 2020-02-26 21:14
     */
    public class LogHandlerProxy implements MethodInterceptor {
    
        // 目标对象
        private Object target;
    
        public LogHandlerProxy(Object target) {
            this.target = target;
        }
    
        public Object bind() {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(target.getClass());
            enhancer.setCallback(this);
            return enhancer.create();
        }
    
        /**
         * @param object 表示要进行增强的对象
         * @param method 表示拦截的方法
         * @param args 数组表示参数列表,基本数据类型需要传入其包装类型
         * @param methodProxy 用于调用父类方法
         */
        @Override
        public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            before();
            Object result = method.invoke(target, args);
            after();
            return result;
        }
    
        public void before() {
            System.out.println("日志采集...");
        }
    
        public void after() {
            System.out.println("执行成功...");
        }
    }
    

    需要关注的几点是:

    • 动态代理的实现需要实现MethodInterceptor接口,接口中只有一个方法intercept。方法中四个参数已经分别在代码中注明
    • 由于动态代理所代理的类是不确定的,所以targetObject类型
    • 代理对象的产生是通过Enhancer#create方法

    测试代码

    /**
     * @author sicimike
     * @create 2020-02-26 21:19
     */
    public class CglibDynamicProxyDemo {
    
        public static void main(String[] args) {
            RentServiceImpl proxy = (RentServiceImpl) new LogHandlerProxy(new RentServiceImpl()).bind();
            proxy.signContract();
        }
    }
    

    执行结果

    日志采集...
    签署合同....
    执行成功...
    

    所以Cglib动态代理是靠字节码增强技术来实现的,核心类就是MethodInterceptorEnhancer

    JDK动态代理和Cglib动态代理

    • Bean有实现接口时,Spring就会使用JDK实现动态代理
    • Bean没有实现接口时,Spring使用Cglib实现动态代理
    • 可以强制使用Cglib,在Spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

    代理模式优点

    • 代理模式能将代理对象与真实被调用的目标对象分离
    • 一定程度上降低了系统的耦合度,扩展性好
    • 保护目标对象
    • 增强目标对象

    代理模式缺点

    • 代理模式会造成系统设计中类数目的增加
    • 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢
    • 增加系统复杂度

    源代码

    Proxy

    总结

    代理模式是一种非常重要的设计模式,Spring核心组件AOP,就是通过动态代理来实现的。所以学好了动态代理,就学好了一半的Spring。
    张学友

    展开全文
  • java代理模式-原来你是这样的代理

    千次阅读 热门讨论 2017-03-26 23:15:03
    这几天在看一些框架源码时看到了一个很奇妙的设计模式,有种熟悉个感觉,一时想不出是什么模式,后面经过了解才知道是动态代理,就这样带着好奇心学习了这个模式,更深入了解代理会发现不仅有静态和动态,还有很多其他的...

    这几天在看一些框架源码时看到了一个很奇妙的设计模式,有种熟悉个感觉,一时想不出是什么模式,后面经过了解才知道是动态代理,就这样带着好奇心学习了这个模式,更深入了解代理会发现不仅有静态和动态,还有很多其他的代理类别,果然兴趣是最好的老师,效率不错,下面是我一些总结.

    一起来体验下,你也会发现,原来你是这样的代理.

    这里写图片描述

    什么是代理?

    在<大话设计模式>中说到,代理模式,为其他对象提供一种代理以控制对这个对象的访问.

    下面通过一个例子,说明下.

    商家需要搞活动,请了陈奕迅过来商演唱歌,那么商家需要跟陈奕迅进行面谈->签合同->首付款->安排行程->唱歌->收尾款.

    • 在不使用代理的情况下是这样的.
      这里写图片描述
      可以看到陈奕迅好像很忙,除了要唱歌还要做很多的交互,这样我的爱豆不是要忙死了.

    • 使用代理模式
      这里写图片描述
      通过代理人来处理一些琐碎的事情后,陈奕迅就只要负责他独有的功能唱歌就行了,这样间接的访问对象,有效的减轻了一些重复的操作.

    代理的核心角色

    我们可以将代理模式分为三大角色:

    • 抽象角色
      • 代理角色真实角色的公共方法都会在这里定义.
    • 真实角色
      • 供给代理角色用,这里实现了抽象角色的业务逻辑.
      • 关注真正的业务逻辑
    • 代理角色
      • 真实角色的代理,根据真实角色的业务逻辑来实现
        抽象角色的抽象方法,并可以附件自己的操作.

    java中的代理

    代理模式(Proxy)是一种结构型设计模式,主要解决的问题是:在直接访问对象时带来的问题.
    它也是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.

    在java中动态代理机制以巧妙的方式实现了代理模式的设计理念,因此java中也分为动态代理静态代理.

    • 静态代理(静态定义代理类)
    • 动态代理(动态生成代理类)
      • 反射机制,JDK自带的动态代理
      • 通过InvocationHandler(处理器接口)
        • 使用invoke方法实现对真实角色的代理访问.
        • 每次通过Proxy生产代理类对象时,都要指定的处理器对象.
      • java动态性这块可以深入看看,反射与javassist

    静态代理(StaticProxy)

    借用Eason-S大神的类图,静态代理UML类图
    这里写图片描述

    talk is cheap,根据上面陈奕迅的案例来写个demo

    • 抽象角色
    /*
     * 明星和代理人的公共接口
     */
    public interface StarInterface {
    
        // 面谈
        void interview();
    
        // 签合同
        void signContract();
    
        // 首付款
        void firstPayment();
    
        // 安排行程
        void plan();
    
        // 唱歌
        void sing();
    
        //收尾款
        void finalPayment();
    }
    • 真实角色
    /*
     * 明星实现类
     */
    public class EasonStar implements StarInterface {
    
        private final String STAR_NAME = "陈奕迅:";
    
        @Override
        public void sing() {
            System.out.println(STAR_NAME+"唱歌");
        }
    
        @Override
        public void finalPayment() {
            System.out.println(STAR_NAME+"收尾款");
        }
    
        @Override
        public void signContract() {
            System.out.println(STAR_NAME+"签合同");
        }
    
        @Override
        public void plan() {
            System.out.println(STAR_NAME+"安排行程");
        }
    
        @Override
        public void interview() {
            System.out.println(STAR_NAME+"面谈");
    
        }
    
        @Override
        public void firstPayment() {
            System.out.println(STAR_NAME+"预付款");
        }
    }
    • 代理角色
    public class ProxyStar implements StarInterface {
        //私有化被代理角色
        private StarInterface star;
        private final String PROXY_NAME = "代理人:";
    
        /*
         * 使用接口的方式来指向真实角色(多态特性)
         */
        public ProxyStar(StarInterface star) {
            super();
            this.star = star;
        }
    
        @Override
        public void sing() {
            // 唱歌是明星的特有方法,代理是没有的,因此需要明星自己来
            // 调用陈奕迅的唱歌方法...
            star.sing();
        }
    
        @Override
        public void finalPayment() {
            System.out.println(PROXY_NAME+"签尾款");
        }
    
        @Override
        public void signContract() {
            System.out.println(PROXY_NAME+"签合同");
        }
    
        @Override
        public void plan() {
            System.out.println(PROXY_NAME+"安排行程");
        }
    
        @Override
        public void interview() {
            System.out.println(PROXY_NAME+"面谈");
        }
    
        @Override
        public void firstPayment() {
            System.out.println(PROXY_NAME+"预付款");
        }
    }
    • 客户端类
    public class Client {
    
        public static void main(String[] args) {
            //找到陈奕迅
            EasonStar realStar = new EasonStar();
            //找到代理人,专门为陈奕迅代理
            ProxyStar proxyStar = new ProxyStar(realStar);
            //代理人来完成
            proxyStar.interview();
            proxyStar.signContract();
            proxyStar.firstPayment();
            proxyStar.plan();
            //这里调用的是 EasonStar的sing方法
            proxyStar.sing();
            proxyStar.finalPayment();
        }
    }

    输出结果:

    代理人:面谈
    代理人:签合同
    代理人:预付款
    代理人:安排行程
    陈奕迅:唱歌
    代理人:签尾款

    从上面静态代理demo中,你会发现无论代理角色真实角色都需要实现接口,并且将真实角色的细节向调用方完全隐藏,可以看下EasonStar类里面有很多方法,但最终被掉用的只有sing(),其他都是代理角色来调用的.

    动态代理(DynamicProxy)

    动态代理的思维模式与之前的一般模式是一样的,也是面向接口进行编码,创建代理类将具体类隐藏解耦,不同之处在于代理类的创建时机不同,动态代理需要在运行时因需实时创建.

    这里写图片描述

    看下代码.
    由于抽象角色真实角色的代码跟上面静态代理是一样的这里就直接给出代理角色和客户端类的代码.

    • 代理角色
    /**
     * 动态代理角色 主要代理真实角色的方法,
     * 被调用的方法都会走 invoke()方法,可以在该方法中处理真实角色的业务逻辑
     * 
     * @author relicemxd
     *
     */
    public class StarHandler implements InvocationHandler {
       //私有的被代理角色
        private Object star;
        private final String PROXY_NAME = "代理人:";
    
        /*
         * Obj 传入的是需要被代理的真实角色
         * 并且通过下面的反射技术获取到要代理的行为
         */
        public StarHandler(Object star) {
            super();
            this.star = star;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            // 所有掉代理类的方法都会走这里
    
            // TODO 1. 在转调具体目标对象之前,可以执行一些预处理逻辑
            System.out.println(PROXY_NAME + "面谈");
            System.out.println(PROXY_NAME + "签合同");
            System.out.println(PROXY_NAME + "预付款");
            System.out.println(PROXY_NAME + "安排行程");
    
            Object invoke = null;
            // 因为proxy每调用的方法都会走这里, 因此就可以通过 invoke的特性来做一些逻辑判断
            if (method.getName().equals("sing")) {
                // TODO 2. 转调具体目标对象的方法
                // 只有当代理对象调用到了sing的方法,才进入
                invoke = method.invoke(star, args);
            }
            // TODO 3.在转调具体目标对象之后,可以执行一些后处理逻辑
            System.out.println(PROXY_NAME + "收尾款");
    
            return invoke;
        }
    }
    • 客户端类
    public class Client {
        public static void main(String[] args) {
            // 陈奕迅准备要找代理(真实角色)
            EasonStar star = new EasonStar();
            // 找到了这个代理人代理(代理角色)
            StarHandler handler = new StarHandler(star);
    
            // jdk提供的代理实例
            StarInterface proxyStar = (StarInterface) Proxy.newProxyInstance(
                    ClassLoader.getSystemClassLoader(), // 类加载器
                    new Class[] { StarInterface.class }, // 这里必须是接口,否则会报错
                    handler);// 代理类
    
            // 调用interview 会进入StarHandler类的`invoke方法`,
            // 但是interview方法不会被执行
            // proxyStar.interview();
            // 这里调用的是 EasonStar的sing方法
            proxyStar.sing();
            // proxyStar.finalPayment();
        }
    }

    通过动态代理可以看出对抽象角色我们无需知道他是什么时候创建的,也不用知道改接口实现了什么,并且实现的是InvocationHandler接口,接口的唯一方法invoke()用于处理真实角色的逻辑.

    静态代理vs动态代理?

    可以从代理的接口和创建过程来分析下他们的不同之处,进一步了解下代理模式.

    代理中的接口

    • 共同之处

      • 都会在代理角色中会创建一个私有的成员变量
      • 都需要通过接口来实现代理,主要利用java多态的特性
    • 不同之处

      • 静态代理的真实角色代理角色都会实现同一接口(抽象角色),动态代理则只有真实角色实现了接口.
      • 静态代理利用了java的多态特性来实现代理模式,而动态代理巧妙的使用了jdk的反射机制来完成代理,也因此两者区别在于代理角色的实现方式不一样,看下面这条描述.
      • 静态代理的代理角色实现的是抽象角色这个接口,而动态代理实现的是jdk的内置接口InvocationHandler.

    创建代理的过程

    • 共同之处

      • 两者的创建原理一致,需要通过创建代理角色来处理真实角色的一些业务逻辑.
    • 不同之处

      • 静态代理可以直接编码创建,而动态代理是利用反射机制来抽象出代理类的创建过程.
      • 静态代理我们知根知底,要对哪个接口、哪个实现类来创建代理类,所以我们在编译前就直接实现与实现类相同的接口,直接在实现的方法中调用实现类中的相应(同名)方法即可;而动态代理不同,我们不知道它什么时候创建,也不知道要创建针对哪个接口、实现类的代理类(因为它是在运行时因需实时创建的).
      • 在客户端中静态代理利用接口的多态来调用被代理方法,而动态代理则比较复杂通过Proxy.newProxyInstance来创建一个代理实例从而进行代理.这里有人会问使用的不是反射吗?也没跟接口有关联,其实同样也是使用了多态.使用接口指向代理类的实例,最后会用该实例来进行具体方法的调用即可.

    优缺点是什么?

    优点:

    • 拓展新好
      动态代理,不需要更改原有的代码,能在运行过程中根据接口的类型动态的调用真实角色,符合开闭原则.

    • 解耦
      代理角色可以说是一个中介,隔离了客户端和真实角色.

    缺点:

    • 代码量大
      静态代理,需要接口和类,有比较多的重复代码,降低了维护性.

    • 编译效率?
      动态代理,使用的是反射机制相对效率会降低,但实际差别如何,见下面测试代码.

    • 代码可读性
      都是对于接口实现进行代理,因此代码的可读性都不会很好.

    两者的效率如何?

    下面我对代理sing()方法进行了代理测试.

    public class Client {
        public static void main(String[] args) throws Exception {
            // 陈奕迅准备要找代理(真实角色)
            EasonStar star = new EasonStar();
    
            long start = System.currentTimeMillis();
    
            int threadNum = 10;
            CountDownLatch latch = new CountDownLatch(threadNum);
    
            for (int i = 0; i < threadNum; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 1000; i++) {
                            dynamicProy(star);// 163
    
                            // staticProxy(star);//96
                        }
                        latch.countDown();
                    }
                }).start();
            }
    
            latch.await();
    
            long end = System.currentTimeMillis();
    
            System.out.println("总共耗时:" + (end - start));
        }
    
        public static void dynamicProy(EasonStar star) {
            // 找到了这个代理人代理(代理角色)
            StarHandler handler = new StarHandler(star);
            // jdk提供的代理实例
            StarInterface proxyStar = (StarInterface) Proxy.newProxyInstance(
                    ClassLoader.getSystemClassLoader(), // 类加载器
                    new Class[] { StarInterface.class }, // 这里必须是接口,否则会报错
                    handler);// 代理类
    
            proxyStar.sing();
        }
    
        public static void staticProxy(EasonStar star) {
            // 找到代理人,专门为陈奕迅代理
            ProxyStar proxyStar = new ProxyStar(star);
            proxyStar.sing();
        }
    }

    输出结果:

    dynamicProy(star);// 163ms
    staticProxy(star);// 96ms

    静态代理的效率稍微会比动态代理快一些,不过也没有差别很大,因此在选择代理模式类别时,最好还是根据项目需求来筛选出合适的代理模式.

    代理的应用场景?

    那么代理在哪里会用到呢?如果你的项目有这几个方面的需求可以考虑使用.

    • 当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以使用虚拟代理,例如一个对象需要很长时间才能完成加载时。

    • 当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理。通过使用缓冲代理,系统无须在客户端每一次访问时都重新执行操作,只需直接从临时缓冲区获取操作结果即可。

    • 当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理。

    • 当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理。


    代理的分类

    当然除了我最常用的静态,动态代理之外根据代理的实现与目标不同还可以分成下面几种代理,具体见其他代理模式

    • 安全代理:
      屏蔽对真实角色的直接访问.
    • 远程代理:
      通过代理角色远程方法调用(RMI)
    • 延迟加载:
      先加载轻量级代理角色,真正需要再加载真实角色.
    • 虚拟代理
      允许内存开销较大的对象在需要的时候创建.只有我们真正需要这个对象的时候才创建.
    • 保护代理
      为不同的客户提供不同级别的目标对象访问权限.

    如,你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文洁时不能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开.

    项目中有没使用过呢?

    在android中目前很热门的一个网路工具retrofit源码中也使用了动态代理.

    public <T> T create(final Class<T> service) {
        Utils.validateServiceInterface(service);
        if (validateEagerly) {
          eagerlyValidateMethods(service);
        }
        return (T) 
    
    //这里是不是是曾相识呢
    Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
            new InvocationHandler() {
              private final Platform platform = Platform.get();
              @Override public Object invoke(Object proxy, Method method, Object... args)
                  throws Throwable {
                // If the method is a method from Object then defer to normal invocation.
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                if (platform.isDefaultMethod(method)) {
                  return platform.invokeDefaultMethod(method, service, proxy, args);
                }
                return loadMethodHandler(method).invoke(args);
              }
            });
    }

    参考:
    java静态代理与动态代理

    公共技术点之 Java 动态代理

    设计模式(结构型)之代理模式

    每天设计模式-代理模式

    静态代理VS动态代理

    展开全文
  • Java面试题大全(2020版)

    万次阅读 多人点赞 2019-11-26 11:59:06
    BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。 NIO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路...
  • 设计模式02-动态代理模式

    千次阅读 2017-10-28 13:45:58
    上一篇博客,我们以小明买雪碧为例介绍了代理模式(静态代理)的使用场景和代码实现 静态代理模式介绍现在再来考虑以下假设的生活场景小明 从楼下书店下单买书,书店再向印刷厂下单,积100分; 从楼下五金店下单买...
  • Android设计模式-代理模式

    千次阅读 2017-05-05 19:18:50
    介绍代理模式(Proxy Patter)也称委托模式,属于结构型设计模式。这是一个相当重要的设计模式,Android源码中随处可见。何为代理模式呢?比如每天吃饭时赶进度是常事,就叫公司的同时帮忙买饭,这就是一种代理;...
  • 十一、代理模式 1、概念 代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问。 使用代理模式创建代表(representative)对象,让代表对象控制某对象的访问,被代理的对象可以是远程的对象、创建开销...
  • 前端面试题

    万次阅读 多人点赞 2019-08-08 11:49:01
    前端面试题汇总 ... 你做的页面在哪些流览器测试过?这些浏览器的内核分别是什么?...它和Standards模式有什么区别 21 div+css的布局较table布局有什么优点? 22 img的alt与title有何异同? strong与em的异同? 22 你能...
  • LTE概述

    千次阅读 2019-10-09 17:05:00
    ,Femto网关,完成S1口控制面板/用户面相关流程的代理服务功能 HeMS :Femto网管系统,提供对HeNB的接入认证、告警、性能等的管理功能 AAA+HSS :与SeGW相连实现HeNB USIM卡安全认证 CN-OSS :实现对SeGW、...
  • JAVA面试笔记

    千次阅读 多人点赞 2019-03-07 17:52:40
    JAVA面试笔记Java基础面试1、...10、JDK动态代理和Cglib的区别?Spring面试题Spirng基础面试1、什么是Spring?2、使用Spring的好处?3、Spring由哪些模块组成?4、核心容器(应用上下文) 模块。5、 BeanFactory –...
  • 在23种设计模式中有一种模式就是代理模式,这里打算对这种模式整理一下,并从源码的角度分析一下对象是如何生成出来的。个人觉得这个模式是23种当中最难的一种,深研究的话需要追述到jvm,这里尽量讲明白,不足之处...
  • SSM面试题

    千次阅读 多人点赞 2019-07-11 19:13:58
    代理模式:关于Aop的实现通过代理模式 8、IOC,AOP的实现原理? IOC:通过反射机制生成对象注入 AOP:动态代理 二、SpringMvc面试题 1、SpringMvc 的控制器是不是单例模式,如果是,有什么问题,怎么解决? ...
  • C#基础教程-c#实例教程,适合初学者

    万次阅读 多人点赞 2016-08-22 11:13:24
    C#基础教程-c#实例教程,适合初学者。 第一章 C#语言基础 本章介绍C#语言的基础知识,希望具有C语言的读者能够基本掌握C#语言,并以此为基础,能够进一步学习用C#语言编写window应用程序和Web应用程序。...
  • 代理模式(Proxy)

    千次阅读 2014-09-25 19:56:47
     代理模式引入一个Proxy对象来解决这个问题,刚开始只有用户编号和姓名的时候,不是一个完整的用户对象,而是一个代理对象,当需要访问完整的用户数据的时候,代理会从数据库中重新获取相应的数据,通常情况下是当...
  • 二、结构型模式   1、适配器模式【Adapter】【学习难度:★★☆☆☆,使用频率:★★★★☆】 定义:适配器模式(有时候也称包装样式或者包装)将一个类的接口适配成用户所期待的。一个适配允许通常因为接口不...
  • 1、定义: 为其他对象提供一种代理以控制对这个对象的访问。 2、使用: 在某些情况下,一个对象不适合或者不能直接引用另一个对象,而...代理分成远程代理、虚代理、保护代理和智能指针; 以下写了一个简单的demo;
  • java动态链模式和责任链模式

    千次阅读 2018-08-06 17:53:05
    动态代理模式 代理,顾名思义,代为处理。先来看看名词解释: 代理就是定义一个行为和某类(class)的对象相似,而又潜在地表示了所有继承自该类的对象的东西,我们把这种类的对象叫做代理(surrogate) 来源代理...
  • 这篇文章举例很是恰当,对于理解代理模式很有帮助。例子是,老板要开会,但是呢,开会之前的工作比如通知到会人员,布置场地以及开会后的收尾工作,这些都可以交给秘书来做,此时,老板是被代理对象,老板把布置场地...
  • 代理模式在所需对象和用户代码之间增加了一层对象,这个对象被称为代理。用户代码只需要直接操作代理对象即可。著名的代理模式的例子就是引用计数指针对象,它使得我们对真实对象的操作都需要经过引用计数指针对象。...
  • linux命令大全

    千次阅读 2015-12-24 08:44:14
    Linux命令大全完整版 目 录 目 录... I 1. linux系统管理命令... 1 adduser1 chfn(change finger information)1 chsh(change shell)1 date. ...gitps(gnu interactive tools proce
  • proxy 模式分成3个部分: 1、interface 部分:也就是图1 例子中的order interface。声明了用户需要调用的所有方法。 2、implementation部分:也就是图1例子中的ordeImplementation。 在不涉及数据库操作的情况下,...
  • JavaScript设计模式之装饰者模式

    万次阅读 2019-12-08 23:56:07
    引:什么是装饰者模式 在传统的面向对象语言中,给对象添加功能常常使用继承的方式,但是继承的方式并不灵活,还会带来许多问题:一方面会导致超类和子类之间存在强耦合性,当超类改变时,子类也会随之改变;另一方面...
  • 无线传感器网络复习大纲

    千次阅读 多人点赞 2019-04-30 10:31:40
    第一章 考点: 无线传感器网络组成部分,概念、特点(了解) 分布式特点(了解) 实时性、时效性(了解) 常见拓扑结构(了解) ... WSN几个分层、分层的功能(了解) ...无线传感器概念:无线传感器网络是一种...
  • 商业模式画布

    万次阅读 多人点赞 2018-07-22 18:52:11
    周四周五机缘巧合参加了一场关于商业模式画布的培训,第一次参加趋向于商业方向的培训,一开始并没有抱太大的期望。但是听完两天的课之后,第一次给培训的老师全是满分的评价,因为确实内容非常受用,两天的课程中即...
  • golang 实现HTTP代理和反向代理

    千次阅读 2018-10-18 17:16:13
    代理的核心功能可以用一句话概括:接受客户端的请求,转发到后端服务器,获得应答之后返回给客户端。下图是 《HTTP 权威指南》一书中给出的图例,可以很清晰地说明这一流程: 代理的功能有很多,事实上整个互联网...
  • 一、文章来由一天看完 hf设计模式 中的工厂模式,略微还有点消化不了,于是打算好好探究一下。其实不仅仅是这两个模式之间容易混淆,他们... 鉴于类容过长,我分成两部分来写,第一部分主要是自己对于看 hf设计模式
  • [七]、代理设计模式 一、什么是 delegate delegate是委托模式.委托模式是将一件属于委托者做的事情,交给另外一个被委托者来处理. 一个标准的委托由以下部分组成: (1)协议的声明 我们需要用协议来...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 40,656
精华内容 16,262
关键字:

代理分成模式