0.设计原则
- 以后每个设计模式要是有用到新的设计原则,会提炼到这里
- 思考哪些是会变化的,哪些是不会变化的,封装
- 针对接口编程
- 重组合轻继承是为了runtime扩展!!!!!!!!!!!!!!!!!!
- The Open-Closed Principle: Classes should be open for extension(扩展), but closed for modifi cation(修改).
1 STRATEGY pattern 策略模式
1.1 涉及的OO Principles
- Encapsulate what varies.
- Favor composition over inheritance.
- Program to interfaces, not implementations.
1.2 OO Patterns定义
- The Strategy Pattern defines a family of algorithms,encapsulates each one, and makes them interchangeable.Strategy lets the algorithm vary independently from clients that use it.
1.3 例子:duck fly quack
- Duck父类里要是写了fly() quack()方法 不是所有鸭子都会飞叫,所以不行,要是改成实现接口,那么其他会飞会叫的,由于接口没有实现方法,所以每个类implements这两个接口的写具体实现方法,冗余代码巨多
1.3.1 Zeroing in on the problem...
- take the partsthat vary and encapsulate them, so that later you ca nalter or extend the parts that vary without affecting those that don’t.把变的抽取出来 这差不多是所有dp的准则了
1.3.2 Designing the Duck Behaviors
-
面向接口编程,之前我们的行为都和implementation(实现)绑的死死的(写在superclass,或者在子类中实现特定的接口)
-
“Program to an interface ” really means “Program to a supertype. so that the actual runtime object isn’t locked into the code. which means the class declaring them doesn’t have to know about the actual object types!” 这句话更准确的说:变量的声明类型是supertype,通常是一个抽象类或者接口 Animal animal = new Dog();
-
为了说明我提早用了这张图 这张图其实是下面章节用的
1.3.3 Implementing the Duck Behaviors
-
这样设计 其他类还能复用这两个接口,因为这两个行为已经和鸭子类无关了,然后我们还能定义新的行为,不用改动Duck类
-
这样设计,这两个接口(类) 只有behavior没有state,但是behavior里面也可能有state, a flying behavior might have instance variables representing the attributes for the flying (wing beats per minute, max altitude and speed, etc.) behavior.
-
if you needed to add rocket-powered flying, 这么写class RocketPower implements FlyBehavior
1.3.4 Integrating the Duck Behavior
- The key is that a Duck will now delegate its flying and quacking behavior(委派给别人处理), instead of using quacking and flying methods defined in the Duck class (or subclass)(而不是使用定义在Duck类中的方法).
public abstract class Duck { FlyBehavior flyBehavior; // 每只鸭子的实例都有一个 具体的 behavior的 implementation QuackBehavior quackBehavior; public Duck() {} //可以在运行时候动态改变 public void setFlyBehavior(FlyBehavior fb) { fl yBehavior = fb; } public void setQuackBehavior(QuackBehavior qb) { quackBehavior = qb; } // more 定义了这个行为供子类调用 一个 委派 :鸭子对象自己不亲自处理Quack,交给QuackBehavior对象处理 public void performQuack () { quackBehavior.quack(); } public void performFly() { flyBehavior.fly(); }}public interface FlyBehavior { public void fly();}public class FlyWithWings implements FlyBehavior { public void fly() { System.out.println(“I’m flying!!”); }}
public class MallardDuck extends Duck { public MallardDuck () { //这里的Quack,FlyWithWings类都是实现了接口 //我觉得写成set方法 或者直接传入更好把? public MallardDuck (QuackBehavior quackBehavior,FlyBehavior flyBehavior) {} quackBehavior = new Quack(); //这里具体的实现都可以变得 比如Squack(),NoQuack()等等 flyBehavior = new FlyWithWings(); } public void display () { System.out.println("I’m a real Mallard duck"); }}//调用Duck mallard = new MallardDuck();mallard.performQuack();mallard.performFly();mallard.setFlyBehavior(new FlyRocketPowered());mallard.performFly();
- 一个问题:we should NOT program to an implementation? But what are we doing in that constructor? We’re making a new instance of a concrete Quack implementation class! 我们会在后面的章节解决
1.4 总结
- 策略模式主要就是把行为组合到对象中,把行为外包出去.这里是把各个种类鸭子的一些会变的行为封装外包出去成为策略再组合进来.
2 the Observer Pattern
2.1 设计OO Principles
- Strive for loosely coupled designs between objects that interact.
2.2 OO Patterns定义
-
The Observer Pattern defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically.
-
对于观察者模式:会变的部分是subject的states和observer的数量;subject和observer都针对接口编程,主题利用观察者接口通知观察者;主题和观察者之间是用组合,运行时确定的;
2.3 例子:气象观测站:需求
2.3.1 第一个错误的实例
- 这里主要是如果有新的Display,要改动WeatherData的代码添加进来,如果使用观察者模式,Display的行为和WeatherData这个类几乎是完全解耦的
public class WeatherData { // instance variable declarations public void measurementsChanged () { float temp = getTemperature(); float humidity = getHumidity(); float pressure = getPressure(); currentConditionsDisplay.update( temp, humidity, pressure ); statisticsDisplay.update( temp, humidity, pressure ); forecastDisplay.update( temp, humidity, pressure ); }// other WeatherData methods here}
- 错误点:
- 7 8 9行是变化的部分,我们应该封装起来.
- 7 8 9行他们是对具体实现编程,我们不能增加或者删除实现却不改动代码的.
- 7 8 9行的update方法很相似,所以他们可以是一个接口
2.3.2 The Observer Pattern defined: the class diagram
- 注意 是一个Subject里包含有多个Observer 箭头是Subject指向Observer(只是回顾下组合的指向)
2.3.3 The power of Loose Coupling
- When two objects are loosely coupled, they can interact(交互),but have very little knowledge of each other.
- Subject will deliver notifications to any object that implements the Observer interface.
2.3.4 气象站的Observer Pattern
2.3.4.1 接口的定义
interface Subject { //这样就和Observer有了一个耦合 但是注意不是subject主动调用registerObserver,而是Observer里面有一个subject的引用,然后Observer自己调用这个方法把自己注册到subject中去的 public void registerObserver ( Observer observer ); public void removeObserver ( Observer observer ); public void notifyObservers ();}interface Observer { //这里传入的参数最好要封装成对象 void update ( float temp, float humidity, float pressure );}interface DisplayElement { void display ();}
2.3.4.2 subject的实现 后面的observe都和这个关联
public class WeatherData implements Subject { private List< Observer > observers; private float temperature; private float humidity; private float pressure; public WeatherData () { this.observers = new ArrayList<>(); } @Override public void registerObserver ( Observer observer ) { observers.add( observer ); } @Override public void removeObserver ( Observer observer ) { int i = observers.indexOf( observer ); if ( i >= 0 ) { observers.remove( i ); } } @Override public void notifyObservers () { for ( Observer observer : observers ) { observer.update( temperature, humidity, pressure ); } } public void measurementsChanged () { notifyObservers(); } public void setMeasurements ( float temperature, float humidity, float pressure ) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; measurementsChanged(); } // other WeatherData methods here}
2.3.4.3 observe的实现
- 这里只给出第一个
public class CurrentConditionsDisplay implements Observer, DisplayElement { private float temperature; private float humidity; private float pressure; private Subject weatherData; public CurrentConditionsDisplay ( Subject weatherData ) { this.weatherData = weatherData; //这里保存了一个subject的引用..以后你可以取消订阅 这就王权和subject()没啥关系了 subject()完全不care你 weatherData.registerObserver( this ); } @Override public void display () { System.out.println( "Current conditions: " + temperature + "F degrees and " + humidity + "% humidity" ); } //这个update方法会被被subject调用 @Override public void update ( float temp, float humidity, float pressure ) { this.temperature = temp; this.humidity = humidity; this.pressure = pressure; //update方法不是防止display()最好的地方 等到看了mvc之后可以更好的写 display(); }}
2.3.4.5 最后的整合使用
public class WeatherStation { public static void main ( String[] args ) { //WeatherData是一个subject(主题) WeatherData weatherData = new WeatherData(); //向Subject注册Observer 可以看到,不用subject主动做啥事 CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay( weatherData ); StatisticsDisplay statisticsDisplay = new StatisticsDisplay( weatherData ); ForecastDisplay forecastDisplay = new ForecastDisplay( weatherData ); //控制权是在Subject手里 weatherData.setMeasurements( 80, 65, 30.4f ); System.out.println( "======================" ); weatherData.setMeasurements( 82, 70, 29.2f ); System.out.println( "======================" ); weatherData.setMeasurements( 78, 90, 29.2f ); }}
2.3.5 在gui中也有应用,简单看下
- 时间监听器其实就是观察者模式
public void go() { JButton button = new JButton(“Should I do it?”); button.addActionListener(new AngelListener()); button.addActionListener(new DevilListener()); frame.getContentPane().add(BorderLayout.CENTER, button); // Set frame properties here}
2.4 总结
- 为何说是松耦合:因为subject不知道observer的细节,只知道他们实现了Observer接口
- subject和observer还是会有一个相互的耦合,observer中有subject的引用是为了register/remove自己,subject中有observer的引用是为了调用observer的update方法,给observer推送消息
- 向subject中注册observer是observer的事情,observer中保存了subject的一个引用,调用subject的register方法,subject中有个observer的列表,调用这个方法会被add到其中,以后要想取消订阅,也是observer自己的事情.可以详细看看上面的使用示例,最后发布消息还是subject的实例来做的
- Subjects, ( Observables ), update Observers using a common interface.
- 感觉就是一个推送的机制,push,不然的话是subject提供get()方法,observer去调用来获取他们需要的信息.push可以一口气push所有的东西
- 回调机制感觉和这个也有点像,观察者模式代表人物是MVC模式
3 the Decorator Pattern
- 通过动态的组合对象,可以写新代码添加新的功能而不用改变遗留代码,所以bug和副作用会少很多
- 代码应该如同晚霞中的莲花一样关闭(免于改变),如同晨曦中的莲花一样开放(能够扩展)
- The decorator adds its own behavior either before and/or after delegating to the object it decorates to do the rest of the job.
3.1 涉及的OO Principles
- Encapsulate what varies.
- Classes should be open for extension but closed for modification.
3.2 OO Patterns定义
- attaches additional responsibilities to an object dynamically.Decorators provide a flexible alternative(替代方案) to subclassing for extending functionality.
3.2.1 Meet the Decorator Pattern
- if the customer wants a Dark Roast with Mocha and Whip, then we’ll:
- Take a DarkRoast object
- Decorate it with a Mocha object
- Decorate it with a Whip object
- Call the cost() method and rely on delegation to add on the condiment costs
- how do you “decorate” an object, and how does delegation come into this?: think of decorator objects as “wrappers.”
-
it’s vital that the decorators have the same type as the objects they are going to decorate.(装饰者和被装饰者必须是一样的类型,这里的继承是为了"类型匹配"而不是利用继承获得行为)当我们在compose a decorator with a component的时候,就在增加新的行为
-
还是那句话,利用继承,类的行为只能在编译时确定,利用组合,可以在运行时
3.3 例子:咖啡馆
- 问题:设计死板,类数量爆炸,基类加入的新功能并不适用于所有子类
- 调整饮料的价钱会改变现有代码
- 出现新的调料,要加上新的方法并且改变cost
- 基类加入的新功能并不适用于所有子类
- 如果客户要一个双倍摩卡咖啡?
3.3.1 装饰器模式设计
- 首先顾客要了一杯DarkRoast(就是最里面的那个圆)
- 顾客要往DarkRoast里加摩卡,于是最里面的圆外面套上了摩卡的圆
- 顾客还要加whip(奶泡),于是再套上一层奶泡
- 计算价格,如下图(但是无论套了多少层,它还是一杯Beverage)
3.3.1.2 具体实现类图
- we’re subclassing the abstract class Beverage in order to have the correct type,not to inherit its behavior.The behavior comes in through the composition of decorators with the base components as well as other decorators.
3.3.1.2 具体实现类
public abstract class Beverage { String description = "Unknow Beverage"; public String getDescription () { return description; } public abstract double cost ();}
public abstract class CondimentDecorator extends Beverage { //调料 public abstract String getDescription ();}
public class Espresso extends Beverage { public Espresso () { description = "Espresso"; } @Override public double cost () { return 1.99; }}
// 是一个decorator CondimentDecorator继承自Beveragepublic class Mocha extends CondimentDecorator { //正因为这边保存了一个引用,才能包装它,又因为本身Mocha也是Beverage,所以能相互代替 Beverage beverage; public Mocha ( Beverage beverage ) { this.beverage = beverage; } @Override public String getDescription () { return beverage.getDescription() + ", Mocha"; } @Override public double cost () { //这边摩卡的定价其实可以写set 不过这么写比较省代码空间 return beverage.cost() + 0.20; }}
- 最后使用,当学过工厂和生成器之后,会有更好的方式建立被装饰对象
public class StarbuzzCoffee { public static void main ( String[] args ) { Beverage beverage = new Espresso(); beverage = new Mocha( beverage ); beverage = new Soy( beverage ); beverage = new Whip( beverage ); //Beverage beverage = new Whip( new Soy( new Mocha( new Espresso() ) ) ); System.out.println( beverage.getDescription() + " price is:" + beverage.cost() ); }}
3.3.2 Real World Decorators: Java I/O
- 当初学io确实也痛苦,然而现在啥都忘了!还有nio aio等待步坑...
3.4 总结
- 装饰器模式...想吐槽缺点...小类太多了,还有增加代码复杂性,这个工厂和生成器模式可以改善
- 装饰器和被装饰者拥有相同的类型
Words
assign 赋值 concrete 具体的 vary 变化 Integrating 整合 delegate 委派 vendor 卖主;厂商;卖方 spec 规格 Subscribers 订阅者