CHANGE(改变)
不管当初软件设计得多好,一段时间之后,总是需要成长和改变,否则软件就会“死亡”
https://www.jianshu.com/p/57620b762160
类图基础属性:
-表示private
#表示protected
_下划线表示static
斜体表示抽象
继承:空心三角形+实线
实现(比如实现接口):空心三角形+虚线
依赖(对于两个相对独立的对象,当一个对象负责构造另一个对象的实例,或者依赖另一个对象的服务时,这两个对象之间主要体现为依赖关系。):虚线箭头
关联(对于两个相对独立的对象,当一个对象的实例与另一个对象的一些特定实例存在固定的对应关系时,这两个对象之间为关联关系):实线箭头
聚合(体现has-a的关系):空心菱形+实线箭头
组合(contains-a的关系):实心菱形+实线箭头
策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
当存在以下情况时使用策略模式:
好:
坏:
有很多品种的鸭子,鸭子有两个行为:飞和叫,对于每个品种,具体的行为不一样。对于这种场景,可以用策略模式
我们需要定义鸭子为抽象类,具体的鸭子会继承这个抽象类,Duck抽象类中需要定义两个私有成员,表示飞和叫的策略,飞和叫的行为是接口,对应不同的具体实现,这些实现是可随时互相替换的,也就是只要改变具体鸭子类对应的策略即可
public abstract class Duck { private FlyBehavior flyBehavior; private QuackBehavior quackBehavior; public void performFly() { flyBehavior.fly(); } public void performQuack() { quackBehavior.quack(); } public abstract void display(); public void setFlyBehavior(FlyBehavior flyBehavior) { this.flyBehavior = flyBehavior; } public void setQuackBehavior(QuackBehavior quackBehavior) { this.quackBehavior = quackBehavior; } public Duck() { } }
public interface FlyBehavior { public void fly(); }
public interface QuackBehavior { public void quack(); }
public class FlyWithWings implements FlyBehavior { @Override public void fly() { System.out.println("I can fly with wings"); } }
public class Quack implements QuackBehavior { @Override public void quack() { System.out.println("quack...quack"); } }
public class MallardDuck extends Duck { @Override public void display() { System.out.println("I am a com.my.StrategyPattern.Object.MallardDuck!"); } public MallardDuck() { this.setFlyBehavior(new FlyWithWings()); this.setQuackBehavior(new Quack()); } }
有一个游戏,这个游戏中玩家可以扮演很多种不同的角色,不同角色可以装备不同的武器,每种武器的攻击行为都不同,但角色和武器有对应关系,比如国王只能用剑,盗贼只能用小刀,但未来可能会出现新的武器可以使用
需要定义角色为抽象类,该抽象类中包含私有成员武器接口,武器接口可以对应不同的具体实现
public abstract class Character { private WeaponBehavior weaponBehavior; public abstract void fight(); public void setWeapon(WeaponBehavior weaponBehavior) { this.weaponBehavior = weaponBehavior; } public WeaponBehavior getWeaponBehavior() { return this.weaponBehavior; } public Character() { } }
public interface WeaponBehavior { public void useWeapon(); }
public class King extends Character { @Override public void fight() { this.getWeaponBehavior().useWeapon(); } public King() { this.setWeapon(new SwordBehavior()); } }
public class SwordBehavior implements WeaponBehavior { @Override public void useWeapon() { System.out.println("I am a saber!"); } }
蝇量模式:Strategy对象通常是很好的轻量级对象
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新
观察者模式中的关键对象是Subject和观察者。一个Subject可以有任意数目的依赖它的观察者。一旦Subject的状态发生改变,所有的观察者都得到通知。
这种交互也称为发布-订阅。Subject是通知的发布者。它发出通知时并不需知道谁是它的观察者。可以有任意数目的观察者订阅并接收通知
在以下任一情况下可以使用观察者模式:
好:
坏:
桥接模式:通过封装复杂的更新语义,ChangeManager充当目标和观察者之间的中介者
单例模式:ChangeManager可使用单例模式来保证它是唯一的并且是可全局访问的
有设备一直在监测天气信息,比如温度和湿度,显示器需要监听天气信息的改变,如果天气信息改变,需要改变自身状态并显示最新的天气
需要定义天气信息为被观察者,显示器为观察者
JDK自带了观察者模式
public class WeatherData extends Observable { private float temperature; private float humidtity; private float pressure; public float getTemperature() { return temperature; } public float getHumidtity() { return humidtity; } public float getPressure() { return pressure; } public void setMessurements(float temperature, float humidtity, float pressure) { this.temperature = temperature; this.humidtity = humidtity; this.pressure = pressure; messurementsChanged(); } public void messurementsChanged() { this.setChanged(); this.notifyObservers(); } }
public interface DisplayElement { public void display(); }
public class CurrentConditionsDisplay implements DisplayElement, Observer { private float temperature; private float humidity; private float pressure; private Observable observable; @Override public void display() { System.out.println(StringUtils.join(new String[]{"current condition : temperature is ", String.valueOf(temperature), "; humidity is ", String.valueOf(humidity)})); } public CurrentConditionsDisplay(Observable observable) { this.observable = observable; observable.addObserver(this); } @Override public void update(Observable o, Object arg) { if (o instanceof WeatherData) { WeatherData weatherData = (WeatherData) o; this.temperature = weatherData.getTemperature(); this.humidity = weatherData.getHumidtity(); this.pressure = weatherData.getPressure(); display(); } } }
需要定义主题接口,该接口需要定义3个方法:注册、移除、通知观察者
天气信息需要实现主题接口,并在内部维护观察者的集合
需要定义观察者接口,该接口需要定义update方法,用于改变观察者的状态
显示器需要实现观察者接口,并在内部维护它所订阅的主题
public interface Subject { public void registObserver(Observer observer); public void removeObserver(Observer observer); public void notifyObservers(); }
public class WeatherData implements Subject { private List<Observer> observerList; private float temperature; private float humidtity; private float pressure; public float getTemperature() { return temperature; } public float getHumidtity() { return humidtity; } public float getPressure() { return pressure; } public WeatherData() { observerList = new ArrayList<>(); } @Override public void registObserver(Observer observer) { observerList.add(observer); } @Override public void removeObserver(Observer observer) { observerList.remove(observer); } @Override public void notifyObservers() { for (Observer observer : observerList) { observer.update(temperature, humidtity, pressure); } } public void setMessurements(float temperature, float humidtity, float pressure) { this.temperature = temperature; this.humidtity = humidtity; this.pressure = pressure; messurementsChanged(); } public void messurementsChanged() { notifyObservers(); } }
public interface Observer { public void update(float temperature, float humidity, float pressure); }
public interface DisplayElement { public void display(); }
public class CurrentConditionsDisplay implements DisplayElement, Observer { private Subject weatherData; private float temperature; private float humidity; private float pressure; public CurrentConditionsDisplay(Subject weatherData) { this.weatherData = weatherData; weatherData.registObserver(this); } @Override public void update(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; display(); } @Override public void display() { System.out.println(StringUtils.join(new String[]{"current condition : temperature is ", String.valueOf(temperature), "; humidity is ", String.valueOf(humidity)})); } }
装饰者模式动态地将责任附加到对象上。若要拓展功能,装饰者提供了比继承更有弹性的替代方案
希望给某个对象而不是整个类添加一些功能
以下情况使用Decorator模式:
Decorator将请求转发给它的Component对象,并有可能在转发请求前后执行一些附加的动作
好:
坏:
java.io包
需要读取一个文本文件,将每个字符转成小写输出,一种方法是创建一个类继承并重写FilterInputStream,Java的io包使用的是装饰者模式,FilterInputStream内部包含InputStream,实际上进行io还是调用InputStream进行io,但是可以进行特殊处理
public class LowerCaseInputStream extends FilterInputStream { public LowerCaseInputStream(InputStream in) { super(in); } @Override public int read() throws IOException { int c = super.read(); return (c == -1 ? c : Character.toLowerCase((char)c)); } @Override public int read(byte[] b, int off, int len) throws IOException { int result = super.read(b, off, len); for (int i = off; i < off + result; i++) { b[i] = (byte)Character.toLowerCase((char)b[i]); } return result; } }
public class InputTest { public static void main(String[] args) throws IOException { int c; try { InputStream in = new LowerCaseInputStream(new BufferedInputStream(new FileInputStream("D:\\cs_file\\java\\DesignPattern\\test.txt"))); while ((c = in.read()) >= 0) { System.out.print((char)c); } in.close(); } catch (IOException e) { e.printStackTrace(); } } }
奶茶店有很多不同品种的饮料,每种饮料有大中小三种规格,并且饮料都可以加料,比如加大豆等等,并且可以加多份,最后需要按照规格和加的料来算最后的价格为多少
需要定义饮料抽象类,该抽象类有cost抽象方法,用于计算价格。需要定义装饰者抽象类,装饰者抽象类继承了饮料抽象类,之后定义具体的饮料类继承自抽象类,定义具体的装饰者类,表示加料,装饰者类内部包含饮料类,装饰者类在调用cost方法时,会去调用内部包含饮料类的cost方法,并且加上料的钱返回
这样装饰者类可以一直装饰下去,不管用户的饮料加了多少种料,加了多少次,都可以无限套娃下去,最后调用cost方法算出的就是最终的价格
public abstract class Beverage { String description = "Unknown Beverage"; final int TALL = 0; final int GRANDE = 1; final int VENTI = 2; private int size; public String getDescription() { return description; } public int getSize() { return this.size; } public void setSize(int size) { this.size = size; } public abstract double cost(); }
public abstract class CondimentDecorator extends Beverage{ @Override public abstract String getDescription(); @Override public abstract int getSize(); }
public class HouseBlend extends Beverage{ public HouseBlend(int size) { description = size + " House Blend Coffee"; setSize(size); } @Override public double cost() { return 0.89; } }
public class Soy extends CondimentDecorator{ Beverage beverage; public Soy(Beverage beverage) { this.beverage = beverage; } @Override public int getSize() { return beverage.getSize(); } @Override public String getDescription() { return beverage.getDescription() + ", Soy"; } @Override public double cost() { int size = beverage.getSize(); double cost = beverage.cost(); if (size == beverage.TALL) { cost += 0.10; } else if (size == beverage.GRANDE) { cost += 0.15; } else if (size == beverage.VENTI) { cost += 0.20; } return cost; } }
public class StarbuzzCoffee { public static void main(String[] args) { Beverage beverage = new Espresso(0); System.out.println(beverage.getDescription() + "$" + beverage.cost()); Beverage beverage2 = new DarkRoast(1); beverage2 = new Mocha(beverage2); beverage2 = new Mocha(beverage2); beverage2 = new Whip(beverage2); System.out.println(beverage2.getDescription() + "$" + beverage2.cost()); Beverage beverage3 = new HouseBlend(2); beverage3 = new Soy(beverage3); beverage3 = new Mocha(beverage3); beverage3 = new Whip(beverage3); System.out.println(beverage3.getDescription() + "$" + beverage3.cost()); } }
工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类
在下列情况下可以使用工厂方法模式:
Creator依赖于它的子类来定义工厂方法,返回一个适当的ConcreteProduct实例
好:
坏:
抽象工厂模式:抽象工厂模式常常用工厂方法来实现
模板方法模式:工厂方法通常在模板方法模式中被调用
原型模式:原型模式不需要创建Creator的子类,但它们通常要求一个针对Product类的Initialize操作。Creator使用Initialize来初始化对象
抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类
在以下情况下可以使用抽象工厂模式:
好:
坏:
工厂方法模式:AbstractFactory类通常用工厂方法实现,但也可用原型模式实现
单例模式:一个具体的工厂通常是一个单例
有一个全美连锁的披萨店,用户可以点不同种类的披萨,对于每个分店,对应种类的披萨的制作方式都不同
需要定义一个抽象的工厂类,定义工厂方法用于返回披萨,定义抽象类披萨,具体的披萨继承该类
public abstract class PizzaStore { public Pizza orderPizza(String type) { Pizza pizza; pizza = createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } abstract Pizza createPizza(String type); }
public abstract class Pizza { String name; String dough; String sauce; ArrayList toppings = new ArrayList(); void prepare() { System.out.println("Preparing " + name); System.out.println("Tossing dough..."); System.out.println("Adding sauce..."); System.out.println("Adding toppings: "); for (int i = 0; i < toppings.size(); i++) { System.out.println(" " + toppings.get(i)); } } void bake() { System.out.println("Bake for 25 minutes at 350"); } void cut() { System.out.println("Cutting the pizza into diagonal slices"); } void box() { System.out.println("Place pizza in official PizzaStore box"); } public String getName() { return name; } }
public class NYPizzaStore extends PizzaStore{ @Override Pizza createPizza(String type) { if ("cheese".equals(type)) { return new NYStyleCheesePizza(); } else if ("veggie".equals(type)) { return new NYStyleVeggiePizza(); } else if ("clam".equals(type)) { return new NYStyleClamPizza(); } else if ("pepperoni".equals(type)) { return new NYStylePepperoniPizza(); } else { return null; } } }
public class NYStyleCheesePizza extends Pizza{ public NYStyleCheesePizza() { name = "NY Style Sauce and Cheese Pizza"; dough = "Thin Crust Dough"; sauce = "Marinara Sauce"; toppings.add("Grated Reggiano Cheese"); } }
单例模式确保一个类只有一个实例,并提供一个全局访问点
对一些类来说,只有一个实例是很重要的。如何才能保证一个类只有一个实例并且这个实例易于被访问?可以让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法
当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时
客户只能通过Singleton的getInstance操作访问一个Singleton的实例
好:
很多模式可以使用单例模式实现,如抽象工厂模式,Builder构造器模式,原型模式
成员变量单例对象一定是私有,并且static和volatile修饰,获取实例方法需要双重判断,第一次判断单例对象是否被创建,如果没有创建,则需要锁住类,并且再判断一次单例对象是否被创建,第一次判断是为了提高效率,如果已经创建好了,就可以直接返回,第二次判断是为了安全,因为另一个线程可能已经创建好了单例对象并释放锁,如果不就行判断,会破坏单例
public class ChocolateBoiler { private boolean empty; private boolean boiled; private static volatile ChocolateBoiler chocolateBoiler; private ChocolateBoiler() { empty = true; boiled = false; } public static ChocolateBoiler getInstance() { if (chocolateBoiler == null) { synchronized (ChocolateBoiler.class) { if (chocolateBoiler == null) { chocolateBoiler = new ChocolateBoiler(); } } } return chocolateBoiler; } }
命令模式将“请求”封装成对象,以便使用不同的请求、队列或日志来参数化其他对象。命令模式也支持可撤销的操作
有时必须向某对象提交请求,但并不知道关于被请求的操作或请求的接受者的任何信息。命令模式可以将请求本身变成一个对象,这个对象可被存储并像其他的对象一样被传递。
当你有如下需求时,可使用命令模式:
好:
日程安排、线程池、队列请求(工作队列等)
日志请求
组合模式:可被用来实现宏命令
备忘录模式:可用来保持某个状态,命令用这一状态来取消它的效果
有一个智能家居遥控器,这个遥控器可以通过按不同按钮操作很多家具,并且可以撤回上一个操作指令,遥控器还有模式按钮,例如party模式,可以同时操作多个家具,也就是宏指令
需要定义命令接口,声明执行和撤回方法,具体的命令和宏命令都实现该接口,对于具体的命令类,内部包含它所控制的家具对象,对于宏命令,内部包含一个命令数组,执行或撤回宏命令即逐个执行或撤回数组中的命令
对于用户,即遥控器而言,需要通过容器维护一组命令,并且保存上一个指令,用于撤销操作
命令接口定义执行和撤回方法
public interface Command { public void execute(); public void undo(); }
public class CeilingFan { public static final int HIGH = 3; public static final int MEDIUM = 2; public static final int LOW = 1; public static final int OFF = 0; String location; int speed; public CeilingFan(String location) { this.location = location; speed = OFF; } public void high() { speed = HIGH; System.out.println(location + " ceiling fan is on high"); } public void medium() { speed = MEDIUM; System.out.println(location + " ceiling fan is on medium"); } public void low() { speed = LOW; System.out.println(location + " ceiling fan is on low"); } public void off() { speed = OFF; System.out.println(location + " ceiling fan is off"); } public int getSpeed() { return speed; } public String getLocation() { return location; } }
public class Light { private String location; public Light(String location) { this.location = location; } public void on() { System.out.println(location + " light is on"); } public void off() { System.out.println(location + " light is off"); } }
public class CeilingFanHighCommand implements Command { CeilingFan ceilingFan; int prevSpeed; public CeilingFanHighCommand(CeilingFan ceilingFan) { this.ceilingFan = ceilingFan; } @Override public void execute() { prevSpeed = ceilingFan.getSpeed(); ceilingFan.high(); } @Override public void undo() { if (prevSpeed == CeilingFan.HIGH) { ceilingFan.high(); } else if (prevSpeed == CeilingFan.MEDIUM) { ceilingFan.medium(); } else if (prevSpeed == CeilingFan.LOW) { ceilingFan.low(); } else if (prevSpeed == CeilingFan.OFF) { ceilingFan.off(); } } }
public class LightOnCommand implements Command { Light light; public LightOnCommand(Light light) { this.light = light; } @Override public void execute() { light.on(); } @Override public void undo() { light.off(); } }
public class MacroCommand implements Command { Command[] commands; public MacroCommand(Command[] commands) { this.commands = commands; } @Override public void execute() { for (int i = 0; i < commands.length; i++) { commands[i].execute(); } } @Override public void undo() { for (int i = 0; i < commands.length; i++) { commands[i].undo(); } } }
public class RemoteControlWithUndo { Command[] onCommands; Command[] offCommands; Command undoCommand; public RemoteControlWithUndo() { onCommands = new Command[7]; offCommands = new Command[7]; Command noCommand = new NoCommand(); for (int i = 0; i < 7; i++) { onCommands[i] = noCommand; offCommands[i] = noCommand; } undoCommand = noCommand; } public void setCommand(int slot, Command onCommand, Command offCommand) { onCommands[slot] = onCommand; offCommands[slot] = offCommand; } public void onButtonWasPushed(int slot) { onCommands[slot].execute(); undoCommand = onCommands[slot]; } public void offButtonWasPushed(int slot) { offCommands[slot].execute(); undoCommand = offCommands[slot]; } public void undoButtonWasPushed() { undoCommand.undo(); } @Override public String toString() { StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("\n------ Remote Control ------\n"); for (int i = 0; i < onCommands.length; i++) { stringBuffer.append("[slot " + i + "] " + onCommands[i].getClass().getName() + " " + offCommands[i].getClass().getName() + '\n'); } stringBuffer.append("[undo] " + undoCommand.getClass().getName()); return stringBuffer.toString(); } }
public class RemoteLoader { public static void main(String[] args) { RemoteControlWithUndo remoteControl = new RemoteControlWithUndo(); Light light = new Light("Living Room"); TV tv = new TV("Living Room"); Stereo stereo = new Stereo("Living Room"); Hottub hottub = new Hottub(); LightOnCommand lightOn = new LightOnCommand(light); StereoOnCommand stereoOn = new StereoOnCommand(stereo); TVOnCommand tvOn = new TVOnCommand(tv); HottubOnCommand hottubOn = new HottubOnCommand(hottub); LightOffCommand lightOff = new LightOffCommand(light); StereoOffCommand stereoOff = new StereoOffCommand(stereo); TVOffCommand tvOff = new TVOffCommand(tv); HottubOffCommand hottubOff = new HottubOffCommand(hottub); Command[] partyOn = {lightOn, stereoOn, tvOn, hottubOn}; Command[] partyOff = {lightOff, stereoOff, tvOff, hottubOff}; MacroCommand partyOnMacro = new MacroCommand(partyOn); MacroCommand partyOffMacro = new MacroCommand(partyOff); remoteControl.setCommand(0, partyOnMacro, partyOffMacro); System.out.println(remoteControl); System.out.println("--- Pushing Macro On ---"); remoteControl.onButtonWasPushed(0); System.out.println("--- Pushing Macro Off ---"); remoteControl.offButtonWasPushed(0); } }
适配器模式将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间
一个应用可能会有一些类具有不同的接口并且这些接口互不兼容,这时我们可以使用适配器模式
以下情况使用适配器模式:
对象适配器依赖于对象组合:
Client在Adapter实例上调用一些操作。接着适配器调用Adaptee的操作实现这个请求
好:
坏:
Java中可以将枚举适配到迭代器
桥接模式:桥接模式的结构与对象适配器类似,但是桥接模式的出发点不同:桥接模式的目的是将接口部分和实现部分分离,从而对它们可以较为容易也相对独立地加以改变。而适配器模式则意味着改变一个已有对象的接口
装饰者模式:装饰者模式增强了其他对象的功能而同时又不改变它的接口。因此装饰者对应用程序的透明性比适配器要好。结果是装饰者decorator支持递归组合,而纯粹使用适配器
是不可能实现这一点的
代理模式:代理模式在不改变它的接口的条件下,为另一个对象定义了一个代理
有不同种类的鸭子和火鸡,鸭子和火鸡的叫声不同,现在要让鸭子学火鸡叫,火鸡学鸭子叫,但因为物种不同,它们最终发出的声音还是不同的
需要定义鸭子和火鸡接口,声明叫方法,具体的鸭子和火鸡实现接口,需要定义鸭子和火鸡的适配器类,需要定义鸭子和火鸡适配器类,鸭子适配器类是为了把鸭子伪装成火鸡,所以它需要实现火鸡接口,成员变量是鸭子对象,在调用火鸡接口方法时,实际上还是调用的鸭子方法,火鸡适配器反之
public interface Duck { public void quack(); public void fly(); }
public interface Turkey { public void gobble(); public void fly(); }
public class WildTurkey implements Turkey { @Override public void gobble() { System.out.println("Gobble gobble"); } @Override public void fly() { System.out.println("I am flying a short distance"); } }
public class MallardDuck implements Duck { @Override public void quack() { System.out.println("Quack"); } @Override public void fly() { System.out.println("I am flying"); } }
public class DuckAdapter implements Turkey { private Duck duck; private Random rand; public DuckAdapter(Duck duck) { this.duck = duck; rand = new Random(); } @Override public void gobble() { duck.quack(); } @Override public void fly() { // 生成一个随机数,该随机数的范围为[0,5) if (rand.nextInt(5) == 0) { duck.fly(); } } }
public class TurkeyAdapter implements Duck { Turkey turkey; public TurkeyAdapter(Turkey turkey) { this.turkey = turkey; } @Override public void quack() { turkey.gobble(); } @Override public void fly() { for (int i = 0; i < 5; i++) { turkey.fly(); } } }
public class DuckTestDrive { public static void main(String[] args) { MallardDuck duck = new MallardDuck(); WildTurkey turkey = new WildTurkey(); Duck turkeyAdapter = new TurkeyAdapter(turkey); System.out.println("The Turkey says..."); turkey.gobble(); turkey.fly(); System.out.println("\nThe Duck says..."); testDuck(duck); System.out.println("\nThe TurkeyAdapter says..."); testDuck(turkeyAdapter); } static void testDuck (Duck duck) { duck.quack(); duck.fly(); } }
自己用迭代器模式实现Emeration和Iterator的互相适配
public class EnumerationIterator implements Iterator { Enumeration enumeration; public EnumerationIterator(Enumeration enumeration) { this.enumeration = enumeration; } @Override public boolean hasNext() { return enumeration.hasMoreElements(); } @Override public Object next() { return enumeration.nextElement(); } }
public class IteratorToEnumeration implements Enumeration { private Iterator iterator; public IteratorToEnumeration(Iterator iterator) { this.iterator = iterator; } @Override public boolean hasMoreElements() { return iterator.hasNext(); } @Override public Object nextElement() { return iterator.next(); } }
外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用
将一个系统划分成为若干个子系统有利于降低系统的复杂性。一个常见的设计目标是使子系统间的通信和相互依赖关系达到最小。达到该目标的途径之一就是引入一个外观对象,它为子系统中较一般的设施提供了一个单一而简单的界面
当遇到以下情况时使用外观模式:
好:
抽象工厂模式:抽象工厂模式可以与外观模式一起使用以提供一个接口,这一接口可用来以一种子系统独立的方式创建子系统对象。
中介者模式:中介者模式与外观模式的相似之处是,它抽象了一些已有的类的功能。然而,中介者的目的是对对象之间的任意通讯进行抽象,通常用于集中不属于任何单个对象的功能。中介者模式中的对象知道中介者并与它通信,而不是直接与其他同类对象通信。相对而言,外观模式仅对子系统对象的接口进行抽象,从而使它们更容易使用;它并不定义新功能,子系统也不知道facade的存在
单例模式:通常来说,只需要一个facade对象,因此facade对象通常属于单例模式
有一个智能家居设备,这个设备负责家庭影院功能,只要用户按下家庭影院按钮,就会控制各种家具以达到影院模式
需要定义一个门面,该门面维护各类设备作为成员变量,对外提供开启和关闭影院模式的方法,具体方法就是操作各个设备
public class HomeTheaterFacade { Amplifier amp; Tuner tuner; DvdPlayer dvd; CdPlayer cd; Projector projector; TheaterLights lights; Screen screen; PopcornPopper popper; public HomeTheaterFacade(Amplifier amp, Tuner tuner, DvdPlayer dvd, CdPlayer cd, Projector projector, TheaterLights lights, Screen screen, PopcornPopper popper) { this.amp = amp; this.tuner = tuner; this.dvd = dvd; this.cd = cd; this.projector = projector; this.lights = lights; this.screen = screen; this.popper = popper; } public void watchMovie(String movie) { System.out.println("Get ready to watch a movie..."); popper.on(); popper.pop(); lights.dim(10); screen.down(); projector.on(); projector.wideScreenMode(); amp.on(); amp.setDvd(dvd); amp.setSurroundSound(); amp.setVolume(5); dvd.on(); dvd.play(movie);