《大话设计模式 上》

  1. 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
  2. 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
  3. 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

简单工厂模式 (静态方法创建并返回实例对象)

  例如,想写一个可以进行加减乘除操作的函数,可以考虑创建OperationAdd、OperationSub、OperationMul、OperationDiv四种类,并定义一个静态的工厂方法,根据传入的+ 、-、*、/、参数实例化四种对象并返回。OperationAdd、OperationSub、OperationMul、OperationDiv都继承并重写了Operation的getResult方法。
  简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节。简单工厂模式结构比较简单,其核心是工厂类的设计,其结构如图1所示:
       在这里插入图片描述

/// 运算类工厂
    /// </summary>
    class OperationFactory
    {
        public static Operation createOperate(string operate)
        {
            Operation oper = null;
            switch (operate)
            {
                case "+":
                    {
                        oper = new OperationAdd();
                        break;
                    }
                case "-":
                    {
                        oper = new OperationSub();
                        break;
                    }
                case "*":
                    {
                        oper = new OperationMul();
                        break;
                    }
                case "/":
                    {
                        oper = new OperationDiv();
                        break;
                    }

                case "sqrt":
                    {
                        oper = new OperationSqrt();
                        break;
                    }
            }

            return oper;
        }
    }

工厂方法模式

  工厂方法模式与之前的简单工厂模式有什么区别呢?简单工厂模式中,每当新增一种运算类型,要在OperationFactory的createOperate方法中修改原有的代码,违背了开放–封闭原则。安装工厂方法模式重新设计,4种不同的运算类都实现getResult方法,同时要响应创建4种运算的工厂类,4种运算的工厂类都继承了工厂类的接口,并实现了返回运算的方法。使用时,先任选一种运算的工厂类,获得返回运算的操作类的实例,再根据运算操作类得到结果。如果要新增一种M的N次方运算,则新建一个M的N次方类,然后新建一个M的N次方工厂用于返回M的N次方类的实例。
  工厂方法模式实现时,客户端需要决定使用哪个工厂类来实现创建运算类,选择判断的问题还是存在的,工厂方法把简单工厂内部判断逻辑移到了客户端代码进行。
在这里插入图片描述

  想象一下,如果要我们设计一个简单的像 log4j 那样的日志框架该怎么设计呢?没有看过设计模式前,你估计会设计这样一个Log1,然后通过调用log1的debug方法即可,但是有一天如果你的日志升级了变成了Log2,你会发现整个工程中都是Log1,我们要每一个都要更改,麻烦不说,还易出错。

public class Log1 {
    public static void debug(String message){
        System.out.println(message);
    }
}
public static main(String[] args)
{
	Log1 log1=new Log1();
	log2.debug("hello");
}

  于是我们做了如下改进。简单工厂模式登场了。

public class LoggerFactory {
    public static Logger getLogger(String logType){
        if("Log1".equals(logType)){
            return new Log1();
        }
        if("Log2".equals(logType)){
            return new Log2();
        }
        return null;
    }
}
public class Client2 {
    Logger logger= LoggerFactory.getLogger(Config.LOG_TYPE);
    public void begin(){
        logger.debug("log");
    }
}  

  但是简单工厂模式有什么问题呢?我们发现每次升级Log类型都要修改Log的工厂LoggerFactory ,这样未被了开放-封闭原则。因此,我们新建一个IFactory 接口,里面有返回Log的方法getLogger,而不同的类型Logger升级时都实现这个接口,同时还要相应创建一个Factory类,LoggerFactory 的成员变量IFacotry 充当简单工厂中多个equls选择不同类型的Log作用。

public interface IFactory {
    Logger getLogger();
}
public class Log1Factory implements IFactory { 
    @Override
    public Logger getLogger() {
        return new Log1();
    }
}
public class LoggerFactory { 
    private static IFactory iFactory;    
    public static Logger getLogger(){
        return iFactory.getLogger();
    }
}

例如:如果有文件使用Logger和数据库连接的Logger,简单工厂模式与工厂方法对比:
        在这里插入图片描述
        在这里插入图片描述

抽象工厂模式

  工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。此时,我们可以考虑将一些相关的产品组成一个“产品族”,由同一个工厂来统一生产,这就是我们本文将要学习的抽象工厂模式的基本思想
  抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形式。抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、更有效率。
   抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,它是一种对象创建型模式。
   在抽象工厂模式中,每一个具体工厂都提供了多个工厂方法用于产生多种不同类型的产品,这些产品构成了一个产品族,抽象工厂模式结构如图5所示:
      在这里插入图片描述
在抽象工厂模式结构图中包含如下几个角色:

  • AbstractFactory(抽象工厂):它声明了一组用于创建一族产品的方法,每一个方法对应一种产品。
  • ConcreteFactory(具体工厂):它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。
  • AbstractProduct(抽象产品):它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法。
  • ConcreteProduct(具体产品):它定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的业务方法。
    在抽象工厂中声明了多个工厂方法,用于创建不同类型的产品,抽象工厂可以是接口,也可以是抽象类或者具体类,其典型代码如下所示:
abstract class AbstractFactory {
public abstract AbstractProductA createProductA(); //工厂方法一
public abstract AbstractProductB createProductB(); //工厂方法二
……
}

  具体工厂实现了抽象工厂,每一个具体的工厂方法可以返回一个特定的产品对象,而同一个具体工厂所创建的产品对象构成了一个产品族。对于每一个具体工厂类,其典型代码如下所示:

class ConcreteFactory1 extends AbstractFactory {
    //工厂方法一
public AbstractProductA createProductA() {
    return new ConcreteProductA1();
} 
//工厂方法二
public AbstractProductB createProductB() {
    return new ConcreteProductB1();
}
}

举例说明: Sunny软件公司欲开发一套界面皮肤库,可以对Java桌面软件进行界面美化。为了保护版权,该皮肤库源代码不打算公开,而只向用户提供已打包为jar文件的class字节码文件。用户在使用时可以通过菜单来选择皮肤,不同的皮肤将提供视觉效果不同的按钮、文本框、组合框等界面元素,其结构示意图如图1所示:
        在这里插入图片描述
若采用工厂方法有一种按钮或者一种文本框就要为其创建一种工厂:显然工厂数目很多。
        在这里插入图片描述
使用抽象工厂模式来重构界面皮肤库的设计,其基本结构如图6所示
     在这里插入图片描述
  代码实现:

	//在本实例中对代码进行了大量简化,实际使用时,界面组件的初始化代码较为复杂,还需要使用JDK中一些已有类,为了突出核心代码,在此只提供框架代码和演示输出。
	//按钮接口:抽象产品
	interface Button {
		public void display();
	}
	 
	//Spring按钮类:具体产品
	class SpringButton implements Button {
		public void display() {
			System.out.println("显示浅绿色按钮。");
		}
	}
	 
	//Summer按钮类:具体产品
	class SummerButton implements Button {
		public void display() {
			System.out.println("显示浅蓝色按钮。");
		}	
	}
	 
	//文本框接口:抽象产品
	interface TextField {
		public void display();
	}
	 
	//Spring文本框类:具体产品
	class SpringTextField implements TextField {
		public void display() {
			System.out.println("显示绿色边框文本框。");
		}
	}
	 
	//Summer文本框类:具体产品
	class SummerTextField implements TextField {
		public void display() {
			System.out.println("显示蓝色边框文本框。");
		}	
	}
	 
	//组合框接口:抽象产品
	interface ComboBox {
		public void display();
	}
	 
	//Spring组合框类:具体产品
	class SpringComboBox implements ComboBox {
		public void display() {
			System.out.println("显示绿色边框组合框。");
		}
	}
	 
	//Summer组合框类:具体产品
	class SummerComboBox implements ComboBox {
		public void display() {
			System.out.println("显示蓝色边框组合框。");
		}	
	}
	 
	//界面皮肤工厂接口:抽象工厂
	interface SkinFactory {
		public Button createButton();
		public TextField createTextField();
		public ComboBox createComboBox();
	}
	 
	//Spring皮肤工厂:具体工厂
	class SpringSkinFactory implements SkinFactory {
		public Button createButton() {
			return new SpringButton();
		}
	 
		public TextField createTextField() {
			return new SpringTextField();
		}
	 
		public ComboBox createComboBox() {
			return new SpringComboBox();
		}
	}
	 
	//Summer皮肤工厂:具体工厂
	class SummerSkinFactory implements SkinFactory {
		public Button createButton() {
			return new SummerButton();
		}
	 
		public TextField createTextField() {
			return new SummerTextField();
		}
	 
		public ComboBox createComboBox() {
			return new SummerComboBox();
		}
	}

策略模式(有多种可选的策略时)

  情景:某商场要制定一个收银系统,计算钱方式不是简单的单价×数量,有时会搞活动打8折,7折,6折,有时有活动满300返50,有时慢700返150,此时如果使用简单工厂模式,先定义一个父类ClassSuper,父类中有方法getResult,然后创建正常收费子类,打折收费子类,返利收费子类,继承ClassSuper并实现getResult方法。在打折收费类中,打几折需要作为该类的初始化参数。
  策略模式:先定义一个策略抽象类Strategy,抽象类中有一个抽象的所有算法公共接口,AlgorithmInterface(),然后不同的策略继承策略类,然后实现AlgorithmInterface方法。当要使用策略模式时,Context类中新建一个Strategy私有成员,然后结合简单工厂方法,根据参数创建不同的策略Strategy子类,并赋值给Strategy成员变量,ContextInterface中调用Strategy成员变量的方法。简单工厂模式多个子类是去继承某一父类,策略模式是多个子类去实现某一抽象的策略接口并结合工厂方法
          在这里插入图片描述

class CashContext
{
    CashSuper cs = null;
    //根据条件返回相应的对象
    public CashContext(string type)
    {
        switch (type)
        {
            case "正常收费":
                CashNormal cs0 = new CashNormal();
                cs = cs0;
                break;
            case "满300返100":
                CashReturn cr1 = new CashReturn("300", "100");
                cs = cr1;
                break;
            case "打8折":
                CashRebate cr2 = new CashRebate("0.8");
                cs = cr2;
                break;
        }
    }
    public double GetResult(double money)
    {
        return cs.acceptCash(money);
    }
}

开放封闭原则
  类可以扩展,但是不可修改。扩展是开放的,但是更改是封闭的,软件设计要容易维护的最好方法,就是多扩展少修改。
单一职责原则
  一个类仅实现单一的功能,而不是将各种功能都集中在一个类中。如果一个类承担的职责过多,等于把这些职责耦合在一起,一个职责发生变化可能会削弱或者抑制这个类完成其他的职责。整合思想固然好,智能手机的摄影功能还是比不过单反的。
依赖倒转原则:依赖指:抽象不应该依赖于细节,细节应该依赖于抽象。强内聚,松耦合。例如PC电脑硬件,CPU、显卡、硬盘都可以看为类,由于PC易插拔,无论哪个出现问题,都可以在不影响其他的前提下进行修改或者替换。为什么叫倒转?例如将访问数据库的代码写出函数,而项目的上层逻辑调用这些函数,这就叫高层模块依赖底层模块。
里氏代还原则:子类型必须能替换掉他们的父类性。
迪米特法则:类之间的耦合越弱,越有利于复用,一个处于弱耦合的类被修改,不会对有关系的类造成波及。在类的设计结构上,每一个类都应当尽量降低成员的访问权限。

装饰模式

  例如,想写一个可以给人搭配不同服饰的系统,怎么开发?
最low的版本:在一个Person类中实现穿T恤、穿球鞋、穿西装、打领带、穿皮鞋、、、、等函数。
第二版:抽象一个服饰类,有show方法,T恤类继承服饰类,西装类继承服饰类,皮鞋继承服饰类、、、都重新show方法,然后需要新建这些类,并依次执行show方法。这种方式相当于当着大家的面,先穿T恤,再穿鞋子,再穿裤子,调用很多个show方法。
装饰模式:抽象一个服饰类Finery,该服饰类除了有show方法,还有继承了Person类(相当于什么都没穿的状态),并且有一个私有变量Person,通过一个Decorate方法将穿了不同的服饰的Person赋值给该成员变量,接下来不同的服饰、裤子、鞋子继承服饰Finery类,(相当于穿了不同服饰的人)。使用时,例如创建一个Person p,再创建一个Finery,Finery可以调用Decorate方法将刚才的人装饰无状态,还可以创建一个穿了T恤的TShirt,TShirt.Decorate§;由于Tshirt继承了服饰Finery类,因此有Person成员,并且通过Decorate§,该成员就是开始的p。

//创建一个Person类
public class Person {
	public Person() {		
	}
	private String name;
	public Person(String name){
		this.name = name;
	}	
	public void show(){
		System.out.println(String.format("装扮的%s", name));
	}
}

再创建一个服饰类:该服饰类中有成员变量Person,并且继承了Person,此处很关键,当后面不同的服饰继承该类时,相当于穿了不同衣服的人,然后调用这个类的Decorate方法,相当于间接的将穿不同服饰的人用类似链表方式构成。

public class Finery extends Person {
	protected Person component;
	public void decorate(Person component) {
		this.component = component;
	}
	@Override
	public void show() {
		if(component != null){
			component.show();
		}
	}
}

具体的服饰类:

public class BigTrouser extends Finery {
	@Override
	public void show() {
		System.out.println("垮裤");
		super.show();
	}
}
public class Sneakers extends Finery {
	@Override
	public void show() {
		System.out.println("球鞋");
		super.show();
	}
}
public class TShirts extends Finery {
	@Override
	public void show() {
		System.out.println("大T恤");
		super.show();
	}
}

测试方法:

public class DemocrateTest {
	public static void main(String[] args) {
		Person xc = new Person("小菜");
			
		Sneakers pqx = new Sneakers();
		BigTrouser kk = new BigTrouser();
		TShirts dtx = new TShirts();
		
		pqx.decorate(xc);		
		kk.decorate(pqx);
		dtx.decorate(kk);
		dtx.show();
	}
}

运行结果:
          在这里插入图片描述

桥接模式

  生活中的一个例子: 就拿汽车在路上行驶的来说。即有小汽车又有公共汽车,它们都不但能在市区中的公路上行驶,也能在高速公路上行驶。这你会发现,对于交通工具(汽车)有不同的类型,然而它们所行驶的环境(路)也在变化,在软件系统中就要适应两个方面的变化?怎样实现才能应对这种变化呢?
  传统的做法: 通过类继承的方式来做上面的例子,先看一下类结构图:根据道路分成市区的路和高速公路,再根据汽车类型不同去继承不同的类型道路,实现不同的跑法,至上到下至少7种类。
        在这里插入图片描述
  仔细分析就可以发现,它还是存在很多问题,首先它在遵循开放-封闭原则的同时,违背了类的单一职责原则,即一个类只有一个引起它变化的原因,而这里引起变化的原因却有两个,即路类型的变化和汽车类型的变化;其次是重复代码会很多,不同的汽车在不同的路上行驶也会有一部分的代码是相同的;再次是类的结构过于复杂,继承关系太多,难于维护,最后最致命的一点是扩展性太差。如果变化沿着汽车的类型和不同的道路两个方向变化,我们会看到这个类的结构会迅速的变庞大。
   桥接模式(Bridge)来做,先看一下类结构图:
        在这里插入图片描述
代码实现:抽象汽车、抽象的路,然后具体的路去继承抽象的路,此次关键的是在路和车如何联系起来,抽象的路中有一个成员是抽象的车,并且在抽象类中就完成了赋值操作。

public abstract class AbstractCar {
	   public abstract void Run();
}
public abstract class AbstractRoad {
	 public AbstractCar car;
	 public abstract void Run();
	 public AbstractRoad(AbstractCar car)
	 {
		 this.car  = car;
	 }
}

具体的路和车的实现:

public class Bus extends AbstractCar{
	public void Run() {
		// TODO Auto-generated method stub
		System.out.println("公交车 ");
	}

}
public class Car extends AbstractCar{
	public void Run() {
		// TODO Auto-generated method stub
		System.out.println("火车 ");
	}
}
public class SpeedWay extends AbstractRoad {
	public SpeedWay(AbstractCar car) {
		super(car);
		// TODO Auto-generated constructor stub
	}
	public void Run() {
		// TODO Auto-generated method stub
		car.Run();
		System.out.println("在高速公路上行驶");
	}
}
public class Street extends AbstractRoad {

	public Street(AbstractCar car) {
		super(car);
		// TODO Auto-generated constructor stub
	}
	public void Run() {
		// TODO Auto-generated method stub
		System.out.println("在市区行驶");
	}

}

测试代码 :

public class BrideModeTest {
	public static void main(String[] args) {
		Car car = new Car();
		SpeedWay speedWay = new SpeedWay(car);
		speedWay.Run();
	}	
}

运行结果:
                    在这里插入图片描述
  在正式介绍桥接模式之前,先跟大家谈谈两种常见文具的区别,它们是毛笔和蜡笔。假如我们需要大中小3种型号的画笔,能够绘制12种不同的颜色,如果使用蜡笔,需要准备3×12 = 36支,但如果使用毛笔的话,只需要提供3种型号的毛笔,外加12个颜料盒即可,涉及到的对象个数仅为 3 + 12 = 15,远小于36,却能实现与36支蜡笔同样的功能。如果增加一种新型号的画笔,并且也需要具有12种颜色,对应的蜡笔需增加12支,而毛笔只需增加一支。为什么会这样呢?通过分析我们可以得知:在蜡笔中,颜色和型号两个不同的变化维度(即两个不同的变化原因)融合在一起,无论是对颜色进行扩展还是对型号进行扩展都势必会影响另一个维度;但在毛笔中,颜色和型号实现了分离,增加新的颜色或者型号对另一方都没有任何影响。如果使用软件工程中的术语,我们可以认为在蜡笔中颜色和型号之间存在较强的耦合性,而毛笔很好地将二者解耦,使用起来非常灵活,扩展也更为方便。在软件开发中,我们也提供了一种设计模式来处理与画笔类似的具有多变化维度的情况,即桥接模式。
  桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式
   桥接模式的结构与其名称一样,存在一条连接两个继承等级结构的桥,桥接模式结构如图10-3所示:
          在这里插入图片描述
在桥接模式结构图中包含如下几个角色:

  • Abstraction(抽象类):用于定义抽象类的接口,它一般是抽象类而不是接口,其中定义了一个Implementor(实现类接口)类型的对象并可以维护该对象,它与Implementor之间具有关联关系,它既可以包含抽象业务方法,也可以包含具体业务方法。
  • RefinedAbstraction(扩充抽象类):扩充由Abstraction定义的接口,通常情况下它不再是抽象类而是具体类,它实现了在Abstraction中声明的抽象业务方法,在RefinedAbstraction中可以调用在Implementor中定义的业务方法。
  • Implementor(实现类接口):定义实现类的接口,这个接口不一定要与Abstraction的接口完全一致,事实上这两个接口可以完全不同,一般而言,Implementor接口仅提供基本操作,而Abstraction定义的接口可能会做更多更复杂的操作。Implementor接口对这些基本操作进行了声明,而具体实现交给其子类。通过关联关系,在Abstraction中不仅拥有自己的方法,还可以调用到Implementor中定义的方法,使用关联关系来替代继承关系。上例中,路就是Abstraction,其调用run方法时,还可以调用车Implemetor中定义的方法。
  • ConcreteImplementor(具体实现类):具体实现Implementor接口,在不同的ConcreteImplementor中提供基本操作的不同实现,在程序运行时,ConcreteImplementor对象将替换其父类对象,提供给抽象类具体的业务操作方法。

   桥接模式是一个非常有用的模式,在桥接模式中体现了很多面向对象设计原则的思想,包括“单一职责原则”、“开闭原则”、“合成复用原则”、“里氏代换原则”、“依赖倒转原则”等。熟悉桥接模式有助于我们深入理解这些设计原则,也有助于我们形成正确的设计思想和培养良好的设计风格。

   在使用桥接模式时,我们首先应该识别出一个类所具有的两个独立变化的维度,将它们设计为两个独立的继承等级结构,为两个维度都提供抽象层,并建立抽象耦合。通常情况下,我们将具有两个独立变化维度的类的一些普通业务方法和与之关系最密切的维度设计为“抽象类”层次结构(抽象部分),而将另一个维度设计为“实现类”层次结构(实现部分)。例如:对于毛笔而言,由于型号是其固有的维度,因此可以设计一个抽象的毛笔类,在该类中声明并部分实现毛笔的业务方法,而将各种型号的毛笔作为其子类;颜色是毛笔的另一个维度,由于它与毛笔之间存在一种“设置”的关系,因此我们可以提供一个抽象的颜色接口,而将具体的颜色作为实现该接口的子类。在此,型号可认为是毛笔的抽象部分,而颜色是毛笔的实现部分,结构示意图如图10-4所示:
          在这里插入图片描述
   在图10-4中,如果需要增加一种新型号的毛笔,只需扩展左侧的“抽象部分”,增加一个新的扩充抽象类;如果需要增加一种新的颜色,只需扩展右侧的“实现部分”,增加一个新的具体实现类。扩展非常方便,无须修改已有代码,且不会导致类的数目增长过快。
  在具体编码实现时,由于在桥接模式中存在两个独立变化的维度,为了使两者之间耦合度降低,首先需要针对两个不同的维度提取抽象类和实现类接口,并建立一个抽象关联关系。对于“实现部分”维度,典型的实现类接口代码如下所示:

interface Implementor {
	public void operationImpl();
}

在实现Implementor接口的子类中实现了在该接口中声明的方法,用于定义与该维度相对应的一些具体方法。对于另一“抽象部分”维度而言,其典型的抽象类代码如下所示:
  

abstract class Abstraction {
	protected Implementor impl; //定义实现类接口对象	
	public void setImpl(Implementor impl) {
		this.impl=impl;
	}	
	public abstract void operation();  //声明抽象业务方法
}

   在抽象类Abstraction中定义了一个实现类接口类型的成员对象impl,再通过注入的方式给该对象赋值,一般将该对象的可见性定义为protected,以便在其子类中访问Implementor的方法,其子类一般称为扩充抽象类或细化抽象类(RefinedAbstraction),典型的RefinedAbstraction类代码如下所示:

class RefinedAbstraction extends Abstraction {
	public void operation() {
		//业务代码
		impl.operationImpl();  //调用实现类的方法
		//业务代码
	}
}

  对于客户端而言,可以针对两个维度的抽象层编程,在程序运行时再动态确定两个维度的子类,动态组合对象,将两个独立变化的维度完全解耦,以便能够灵活地扩充任一维度而对另一维度不造成任何影响。
桥接模式与装饰的区别:
装饰模式:
  这两个模式在一定程度上都是为了减少子类的数目,避免出现复杂的继承关系。但是它们解决的方法却各有不同,装饰模式把子类中比基类中多出来的部分放到单独的类里面,以适应新功能增加的需要,当我们把描述新功能的类封装到基类的对象里面时,就得到了所需要的子类对象,这些描述新功能的类通过组合可以实现很多的功能组合 .
桥接模式
  桥接模式则把原来的基类的实现化细节抽象出来,在构造到一个实现化的结构中,然后再把原来的基类改造成一个抽象化的等级结构,这样就可以实现系统在多个维度上的独立变化 。
  


代理模式

   例如我想找颖宝去演出某部电视剧,颖宝平时的工作很忙,如果什么事情都要自己处理的话,就会显得很劳累。所以才有明星经纪人的出现,那么经纪人其实就是颖宝的代理人的。代理颖宝处理一部分事情,你要找颖宝就必须通过经纪人。
   那么问题就来了,经纪人总要知道颖宝会那些技能吧!不能随便就接了一些活动,颖宝不会的话那么不就相当于捣乱了吗?还没有分担颖宝的压力。对应到Java代码中,就是经纪人(代理人)和颖宝(被代理人)需要有公共的接口。因此需要一个Skill接口,接口中有各种颖宝会的技能,而搭理人YingBaoProxy 除了实现该接口,也要有一个成员变量Skill yb用于描述代理的是谁,哪天跟颖宝闹掰了还可以代理下一个。
   公共技能接口:

/**
 * 代理人和被代理人共识技能类
 */
public interface Skill { 
    /**
     * 唱歌
     * @param name
     */
    void sing(String name);
 
    /**
     * 演出
     * @param name
     */
    void perform(String name);
 
    /**
     * 综艺节目
     * @param name
     */
    void variety(String name);
 
}

  被代理人:

/**
 * 颖宝
 */
public class YingBao implements Skill { 
    @Override
    public void sing(String name) {
        System.out.println("颖宝唱了一首[" + name + "]");
    } 
    @Override
    public void perform(String name) {
        System.out.println("颖宝出演了[" + name + "]");
    } 
    @Override
    public void variety(String name) {
        System.out.println("颖宝上[" + name + "]综艺节目");
    }
 
}

  经纪人:颖宝代理

/**
 * 颖宝经纪人
 */
public class YingBaoProxy implements Skill { 
    //保存被代理人的实例
    private Skill yb;

    public YingBaoProxy(Skill skill) {
        this.yb = skill;
    } 
    //代理人实际是让颖宝去做事情 
    @Override
    public void sing(String name) {
        yb.sing(name);
    }
    @Override
    public void perform(String name) {
        yb.perform(name);
    } 
    @Override
    public void variety(String name) {
        yb.variety(name);
    }
}

  测试:

public class ProxyTest {
	 public static void main(String[] args) {
	        YingBaoProxy ybp = new YingBaoProxy(new YingBao());
	        ybp.sing("想你");
	        ybp.perform("楚乔传");
	        ybp.variety("天天向上");
	    }
}

原型模式

定义:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。原型模式是从一个对象再创建另外一个可定制的对象,而且不用知道任何创建的细节。
类型:创建类模式
类图
      在这里插入图片描述
  原型模式主要用于对象的复制,它的核心是就是类图中的原型类Prototype。Prototype类需要具备以下两个条件:

  1. 实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。
  2. 重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,Prototype类需要将clone方法的作用域修改为public类型。
       原型模式是一种比较简单的模式,也非常容易理解,实现一个接口,重写一个方法即完成了原型模式。在实际应用中,原型模式很少单独出现。经常与其他模式混用,他的原型类Prototype也常用抽象类来替代。
      Prototype类:
public class Prototype implements Cloneable {  
    public Prototype clone(){  
        Prototype prototype = null;  
        try{  
            prototype = (Prototype)super.clone();  
        }catch(CloneNotSupportedException e){  
            e.printStackTrace();  
        }  
        return prototype;   
    }  
}

  需要拷贝的类,首先要继承Prototype

class ConcretePrototype extends Prototype{  
    public void show(){  
        System.out.println("原型模式实现类");  
    }  
} 

  真正使用时,使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。使用原型模式的另一个好处是简化对象的创建,使得创建对象就像我们在编辑文档时的复制粘贴一样简单。 因为以上优点,所以在需要重复地创建相似对象时可以考虑使用原型模式。比如需要在一个循环体内创建对象,假如对象创建过程比较复杂或者循环次数很多的话,使用原型模式不但可以简化创建过程,而且可以使系统的整体性能提高很多。

public class Client {  
    public static void main(String[] args){  
        ConcretePrototype cp = new ConcretePrototype();  
        for(int i=0; i< 10; i++){  
            ConcretePrototype clonecp = (ConcretePrototype)cp.clone();  
            clonecp.show();  
        }  
    }  
} 

运行结果:
                在这里插入图片描述
原型模式的注意事项:
3. 使用原型模式复制对象不会调用类的构造方法。因为对象的复制是通过调用Object类的clone方法来完成的,它直接在内存中复制数据,因此不会调用到类的构造方法。不但构造方法中的代码不会执行,甚至连访问权限都对原型模式无效。单例模式中,只要将构造方法的访问权限设置为private型,就可以实现单例。但是clone方法直接无视构造方法的权限,所以,单例模式与原型模式是冲突的,在使用时要特别注意。
4. 深拷贝与浅拷贝。Object类的clone方法只会拷贝对象中的基本的数据类型(8种基本数据类型byte,char,short,int,long,float,double,boolean),对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。String这个类型需要注意,它是引用数据类型,所以也是浅拷贝。如果想要深拷贝一个对象, 这个对象必须要实现Cloneable接口,实现clone方法,并且在clone方法内部,把该对象引用中的其他对象也要clone一份 , 这就要求这个被引用的对象必须也要实现Cloneable接口并且实现clone方法。例如:

public class Prototype implements Cloneable {  
    private ArrayList list = new ArrayList();  
    public Prototype clone(){  
        Prototype prototype = null;  
        try{  
            prototype = (Prototype)super.clone();  
            prototype.list = (ArrayList) this.list.clone();  
        }catch(CloneNotSupportedException e){  
            e.printStackTrace();  
        }  
        return prototype;   
    }  
}  

  由于ArrayList不是基本类型,所以成员变量list,不会被拷贝,需要我们自己实现深拷贝,幸运的是java提供的大部分的容器类都实现了Cloneable接口。所以实现深拷贝并不是特别困难。

模板方法模式

   模板方法模式是一种基于继承的代码复用技术,它是一种类行为型模式。当我们要完成某一细节层次一致的一个过程或者一系列步骤,但其中个别步骤在更详细的层次上的实现可能会不同,此时考虑采用模板方法模式来处理。模板方法模式就是通过把不变行为搬移到超类,去除子类中的重复代码来体现它的优势,模板方法模式提供了很好的代码复用平台,当不变的和可变行为在方法的子类实现中混在一起,可以通过模板方法模型将不变行为搬移到超类中。模板方法模式结构比较简单,其核心是抽象类和其中的模板方法的设计,其结构如图2所示:例如超类AbstractClass中有不变的模板方法TemplateMethod,TemplateMethod中执行PrimitiveOperation1(),PrimitiveOperation2(),PrimitiveOperation3()三个方法,PrimitiveOperation1(),PrimitiveOperation2(),PrimitiveOperation3()这三个方法在具体的实现类中实现。
                  在这里插入图片描述
由图2可知,模板方法模式包含如下两个角色:

  1. AbstractClass(抽象类):在抽象类中定义了一系列基本操作(PrimitiveOperations),这些基本操作可以是具体的,也可以是抽象的,每一个基本操作对应算法的一个步骤,在其子类中可以重定义或实现这些步骤。同时,在抽象类中实现了一个模板方法(Template Method),用于定义一个算法的框架,模板方法不仅可以调用在抽象类中实现的基本方法,也可以调用在抽象类的子类中实现的基本方法,还可以调用其他对象中的方法。
  2. ConcreteClass(具体子类):它是抽象类的子类,用于实现在父类中声明的抽象基本操作以完成子类特定算法的步骤,也可以覆盖在父类中已经实现的具体基本操作。

外观模式

  外观模式是一种使用频率非常高的结构型设计模式,它通过引入一个外观角色来简化客户端与子系统之间的交互,为复杂的子系统调用提供一个统一的入口,降低子系统与客户端的耦合度,且客户端调用非常方便。
   不知道大家有没有比较过自己泡茶和去茶馆喝茶的区别,如果是自己泡茶需要自行准备茶叶、茶具和开水,如图1(A)所示,而去茶馆喝茶,最简单的方式就是跟茶馆服务员说想要一杯什么样的茶,是铁观音、碧螺春还是西湖龙井?正因为茶馆有服务员,顾客无须直接和茶叶、茶具、开水等交互,整个泡茶过程由服务员来完成,顾客只需与服务员交互即可,整个过程非常简单省事,如图1(B)所示。
         在这里插入图片描述
  在软件开发中,有时候为了完成一项较为复杂的功能,一个客户类需要和多个业务类交互,而这些需要交互的业务类经常会作为一个整体出现,由于涉及到的类比较多,导致使用时代码较为复杂,此时,特别需要一个类似服务员一样的角色,由它来负责和多个业务类进行交互,而客户类只需与该类交互。外观模式通过引入一个新的外观类(Facade)来实现该功能,外观类充当了软件系统中的“服务员”,它为多个业务类的调用提供了一个统一的入口,简化了类与类之间的交互。在外观模式中,那些需要交互的业务类被称为子系统(Subsystem)。如果没有外观类,那么每个客户类需要和多个子系统之间进行复杂的交互,系统的耦合度将很大,如图2(A)所示;而引入外观类之后,客户类只需要直接与外观类交互,客户类与子系统之间原有的复杂引用关系由外观类来实现,从而降低了系统的耦合度,如图2(B)所示。
         在这里插入图片描述
  外观模式中,一个子系统的外部与其内部的通信通过一个统一的外观类进行,外观类将客户类与子系统的内部复杂性分隔开,使得客户类只需要与外观角色打交道,而不需要与子系统内部的很多对象打交道
  外观模式的主要目的在于降低系统的复杂程度,在面向对象软件系统中,类与类之间的关系越多,不能表示系统设计得越好,反而表示系统中类之间的耦合度太大,这样的系统在维护和修改时都缺乏灵活性,因为一个类的改动会导致多个类发生变化,而外观模式的引入在很大程度上降低了类与类之间的耦合关系。引入外观模式之后,增加新的子系统或者移除子系统都非常方便,客户类无须进行修改(或者极少的修改),只需要在外观类中增加或移除对子系统的引用即可。从这一点来说,外观模式在一定程度上并不符合开闭原则,增加新的子系统需要对原有系统进行一定的修改,虽然这个修改工作量不大。
  外观模式中所指的子系统是一个广义的概念,它可以是一个类、一个功能模块、系统的一个组成部分或者一个完整的系统。子系统类通常是一些业务类,实现了一些具体的、独立的业务功能,其简单实现代码如下:

class SubSystemA
{
    public void MethodA()
    {
        //业务实现代码
    }
} 
class SubSystemB
{
    public void MethodB()
    {
        //业务实现代码
     }
} 
class SubSystemC
{
    public void MethodC()
    {
        //业务实现代码
    }
}
class Facade
{
    private SubSystemA obj1 = new SubSystemA();
    private SubSystemB obj2 = new SubSystemB();
    private SubSystemC obj3 = new SubSystemC(); 
    public void Method()
    {
        obj1.MethodA();
        obj2.MethodB();
        obj3.MethodC();
    }
}

建造者模式

  没有人买车会只买一个轮胎或者方向盘,大家买的都是一辆包含轮胎、方向盘和发动机等多个部件的完整汽车。如何将这些部件组装成一辆完整的汽车并返回给用户,这是建造者模式需要解决的问题。建造者模式又称为生成器模式,它是一种较为复杂、使用频率也相对较低的创建型模式。建造者模式为客户端返回的不是一个简单的产品,而是一个由多个部件组成的复杂产品。
  比如此时想设计一套游戏角色系统,游戏角色是一个复杂对象,它包含性别、脸型等多个组成部分,不同的游戏角色其组成部分有所差异。如何一步步创建一个包含多个组成部分的复杂对象,建造者模式为解决此类问题而诞生。
         在这里插入图片描述
  建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一种对象创建型模式
         在这里插入图片描述
在建造者模式结构图中包含如下几个角色:

  • Builder(抽象建造者):它为创建一个产品Product对象的各个部件指定抽象接口,在该接口中一般声明两类方法,一类方法是buildPartX(),它们用于创建复杂对象的各个部件;另一类方法是getResult(),它们用于返回复杂对象。Builder既可以是抽象类,也可以是接口。
  • ConcreteBuilder(具体建造者):它实现了Builder接口,实现各个部件的具体构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象。
  • Product(产品角色):它是被构建的复杂对象,包含多个组成部件,具体建造者创建该产品的内部表示并定义它的装配过程。
  • Director(指挥者):指挥者又称为导演类,它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象(也可以通过配置文件和反射机制),然后通过指挥者类的构造函数或者Setter方法将该对象传入指挥者类中。

  在建造者模式的定义中提到了复杂对象,那么什么是复杂对象?简单来说,复杂对象是指那些包含多个成员属性的对象,这些成员属性也称为部件或零件,如汽车包括方向盘、发动机、轮胎等部件,电子邮件包括发件人、收件人、主题、内容、附件等部件,一个典型的复杂对象类代码示例如下

class Product  {
       private  String partA; //定义部件,部件可以是任意类型,包括值类型和引用类型
       private  String partB;
       private  String partC;
       //partA的Getter方法和Setter方法省略
       //partB的Getter方法和Setter方法省略
       //partC的Getter方法和Setter方法省略
}

  在抽象建造者类中定义了产品的创建方法和返回方法,其典型代码如下:

abstract class Builder {
     //创建产品对象
       protected  Product product=new Product();
      
       public  abstract void buildPartA();
       public  abstract void buildPartB();
       public  abstract void buildPartC();
      
     //返回产品对象
       public  Product getResult() {
              return  product;
       }
}

   在抽象类Builder中声明了一系列抽象的buildPartX()方法用于创建复杂产品的各个部件,具体建造过程在ConcreteBuilder中实现,此外还提供了工厂方法getResult(),用于返回一个建造好的完整产品。
   在ConcreteBuilder中实现了buildPartX()方法,通过调用Product的setPartX()方法可以给产品对象的成员属性设值。不同的具体建造者在实现buildPartX()方法时将有所区别,如setPartX()方法的参数可能不一样,在有些具体建造者类中某些setPartX()方法无须实现(提供一个空实现)。而这些对于客户端来说都无须关心,客户端只需知道具体建造者类型即可。
  在建造者模式的结构中还引入了一个指挥者类Director,该类主要有两个作用:一方面它隔离了客户与创建过程;另一方面它控制产品的创建过程,包括某个buildPartX()方法是否被调用以及多个buildPartX()方法调用的先后次序等。指挥者针对抽象建造者编程,客户端只需要知道具体建造者的类型,即可通过指挥者类调用建造者的相关方法,返回一个完整的产品对象。在实际生活中也存在类似指挥者一样的角色,如一个客户去购买电脑,电脑销售人员相当于指挥者,只要客户确定电脑的类型,电脑销售人员可以通知电脑组装人员给客户组装一台电脑。指挥者类的代码示例如下:

class Director {
       private  Builder builder;
      
       public  Director(Builder builder) {
              this.builder=builder;
       }
      
       public  void setBuilder(Builder builder) {
              this.builder=builer;
       }
      
     //产品构建与组装方法
       public Product construct() {
              builder.buildPartA();
              builder.buildPartB();
              builder.buildPartC();
              return builder.getResult();
       }
}

在指挥者类中可以注入一个抽象建造者类型的对象,其核心在于提供了一个建造方法construct(),在该方法中调用了builder对象的构造部件的方法,最后返回一个产品对象。
  对于客户端而言,只需关心具体的建造者即可,一般情况下,客户端类代码片段如下所示:

Builder  builder = new ConcreteBuilder(); //可通过配置文件实现
Director director = new  Director(builder);
Product product = director.construct();

   可以通过配置文件来存储具体建造者类ConcreteBuilder的类名,使得更换新的建造者时无须修改源代码,系统扩展更为方便。在客户端代码中,无须关心产品对象的具体组装过程,只需指定具体建造者的类型即可。
  建造者模式与抽象工厂模式有点相似,但是建造者模式返回一个完整的复杂产品,而抽象工厂模式返回一系列相关的产品;在抽象工厂模式中,客户端通过选择具体工厂来生成所需对象,而在建造者模式中,客户端通过指定具体建造者类型并指导Director类如何去生成对象,侧重于一步步构造一个复杂对象,然后将结果返回。如果将抽象工厂模式看成一个汽车配件生产厂,生成不同类型的汽车配件,那么建造者模式就是一个汽车组装厂,通过对配件进行组装返回一辆完整的汽车。

观察者模式(实现对象间联动)

  观察者模式是设计模式中的“超级模式”。 在软件系统中,一个对象的状态或行为的变化将导致其他对象的状态或行为也发生改变,它们之间将产生联动,正所谓“触一而牵百发”。为了更好地描述对象之间存在的这种一对多(包括一对一)的联动,观察者模式应运而生,它定义了对象之间一种一对多的依赖关系,让一个对象的改变能够影响其他对象。
  观察者模式是使用频率最高的设计模式之一,它用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应作出反应。在观察者模式中,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间可以没有任何相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。观察者模式的别名包括发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式是一种对象行为型模式。
  观察者模式结构中通常包括观察目标和观察者两个继承层次结构,其结构如图22-3所示:
               在这里插入图片描述
在观察者模式结构图中包含如下几个角色:

  • Subject(目标):目标又称为主题,它是指被观察的对象。在目标中定义了一个观察者集合,一个观察目标可以接受任意数量的观察者来观察,它提供一系列方法来增加和删除观察者对象,同时它定义了通知方法notify()。目标类可以是接口,也可以是抽象类或具体类。
  • ConcreteSubject(具体目标):具体目标是目标类的子类,通常它包含有经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知;同时它还实现了在目标类中定义的抽象业务逻辑方法(如果有的话)。如果无须扩展目标类,则具体目标类可以省略。
  • Observer(观察者):观察者将对观察目标的改变做出反应,观察者一般定义为接口,该接口声明了更新数据的方法update(),因此又称为抽象观察者。
  • ConcreteObserver(具体观察者):在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致;它实现了在抽象观察者Observer中定义的update()方法。通常在实现时,可以调用具体目标类的attach()方法将自己添加到目标类的集合中或通过detach()方法将自己从目标类的集合中删除

下面通过示意代码来对该模式进行进一步分析。首先我们定义一个抽象目标Subject,典型代码如下所示:

import java.util.*;
abstract class Subject {
    //定义一个观察者集合用于存储所有观察者对象
protected ArrayList observers<Observer> = new ArrayList(); 
//注册方法,用于向观察者集合中增加一个观察者
	public void attach(Observer observer) {
    observers.add(observer);
} 
    //注销方法,用于在观察者集合中删除一个观察者
	public void detach(Observer observer) {
    observers.remove(observer);
} 
    //声明抽象通知方法
	public abstract void notify();
}

具体目标类ConcreteSubject是实现了抽象目标类Subject的一个具体子类,其典型代码如下所示:

class ConcreteSubject extends Subject {
    //实现通知方法
	public void notify() {
        //遍历观察者集合,调用每一个观察者的响应方法
		for(Object obs:observers) {
			((Observer)obs).update();
		}
	}	
}

  抽象观察者角色一般定义为一个接口,通常只声明一个update()方法,为不同观察者的更新(响应)行为定义相同的接口,这个方法在其子类中实现,不同的观察者具有不同的响应方法。抽象观察者Observer典型代码如下所示:

interface Observer {
    //声明响应方法
	public void update();
}

  在具体观察者ConcreteObserver中实现了update()方法,其典型代码如下所示:

class ConcreteObserver implements Observer {
    //实现响应方法
	public void update() {
		//具体响应代码
	}
}

  在有些更加复杂的情况下,具体观察者类ConcreteObserver的update()方法在执行时需要使用到具体目标类ConcreteSubject中的状态(属性),因此在ConcreteObserver与ConcreteSubject之间有时候还存在关联或依赖关系,在ConcreteObserver中定义一个ConcreteSubject实例,通过该实例获取存储在ConcreteSubject中的状态。如果ConcreteObserver的update()方法不需要使用到ConcreteSubject中的状态属性,则可以对观察者模式的标准结构进行简化,在具体观察者ConcreteObserver和具体目标ConcreteSubject之间无须维持对象引用。如果在具体层具有关联关系,系统的扩展性将受到一定的影响,增加新的具体目标类有时候需要修改原有观察者的代码,在一定程度上违反了“开闭原则”,但是如果原有观察者类无须关联新增的具体目标,则系统扩展性不受影响。
  实例:软件公司欲开发一款多人联机对战游戏(类似魔兽世界、星际争霸等游戏),在该游戏中,多个玩家可以加入同一战队组成联盟,当战队中某一成员受到敌人攻击时将给所有其他盟友发送通知,盟友收到通知后将作出响应。该系统中战队成员之间的联动过程可以简单描述为: 联盟成员受到攻击–>发送通知给盟友–>盟友作出响应。此时,Player相当于观察者的具体实现,AllyControCenter中有ArrayList存储多个Player,其具体实现ConcreteAllyControlCenter的notifyOberver可以唤醒所有的Player进行响应。
      在这里插入图片描述
  完整代码如下:

import java.util.*; 
//抽象观察类
interface Observer {
	public String getName();
	public void setName(String name);
	public void help(); //声明支援盟友方法
	public void beAttacked(AllyControlCenter acc); //声明遭受攻击方法
} 
//战队成员类:具体观察者类
class Player implements Observer {
	private String name;
 
	public Player(String name) {
		this.name = name;
	}	
	public void setName(String name) {
		this.name = name;
	}	
	public String getName() {
		return this.name;
	}	
    //支援盟友方法的实现
	public void help() {
		System.out.println("坚持住," + this.name + "来救你!");
	}	
    //遭受攻击方法的实现,当遭受攻击时将调用战队控制中心类的通知方法notifyObserver()来通知盟友
	public void beAttacked(AllyControlCenter acc) {
        System.out.println(this.name + "被攻击!");
        acc.notifyObserver(name);		
	}
} 
//战队控制中心类:目标类
abstract class AllyControlCenter {
	protected String allyName; //战队名称
	protected ArrayList<Observer> players = new ArrayList<Observer>(); //定义一个集合用于存储战队成员	
	public void setAllyName(String allyName) {
		this.allyName = allyName;
	}	
	public String getAllyName() {
		return this.allyName;
	}	
    //注册方法
	public void join(Observer obs) {
		System.out.println(obs.getName() + "加入" + this.allyName + "战队!");
		players.add(obs);
	}	
    //注销方法
	public void quit(Observer obs) {
		System.out.println(obs.getName() + "退出" + this.allyName + "战队!");
		players.remove(obs);
	}	
    //声明抽象通知方法
	public abstract void notifyObserver(String name);
}
 //具体战队控制中心类:具体目标类
class ConcreteAllyControlCenter extends AllyControlCenter {
	public ConcreteAllyControlCenter(String allyName) {
		System.out.println(allyName + "战队组建成功!");
		System.out.println("----------------------------");
		this.allyName = allyName;
	}	
    //实现通知方法
	public void notifyObserver(String name) {
		System.out.println(this.allyName + "战队紧急通知,盟友" + name + "遭受敌人攻击!");
        //遍历观察者集合,调用每一个盟友(自己除外)的支援方法
        for(Object obs : players) {
            if (!((Observer)obs).getName().equalsIgnoreCase(name)) {
                ((Observer)obs).help();
            }
        }		
	}
}

  测试代码:

public class ObserveTest {
	public static void main(String args[]) {
		//定义观察目标对象
		AllyControlCenter acc;
		acc = new ConcreteAllyControlCenter("金庸群侠");		
        //定义四个观察者对象
		Observer player1,player2,player3,player4;
		
		player1 = new Player("张无忌");
		acc.join(player1);
		
		player2 = new Player("小昭");
		acc.join(player2);
		
		player3 = new Player("赵敏");
		acc.join(player3);
		
		player4 = new Player("周芷若");
		acc.join(player4);		
		//某成员遭受攻击
		player1.beAttacked(acc);
	}
}

运行结果:
                在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_41262453/article/details/88620284