Java设计模式

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设计模式深入研究》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值