设计模式笔记(上)
代码设计很复杂,但正是因为有了设计模式,才能更简单地理解、描述和解决一个复杂的问题。个人学习设计模式笔记。主要参考《Head
First设计模式》和Wikipedia,youtube上有个系列视频 也非常值得一看
chap 1 设计模式入门—策略模式
定义
策略模式 Strategy Pattern
定义了算法簇,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
分析
环境(Context): 持有一个Strategy的引用
抽象策略(Strategy): 这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口
具体策略(ConcreteStrategy): 包装了相关的算法或行为
利用继承设计子类行为,是在编译时静态决定,而且继承的行为相同;而利用组合可扩展对象的行为,就可在运行时动态地进行扩展。
策略模式=组合composition+委托delegation,实现运行时具有继承行为
设计原则
找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起
针对接口编程,而不是针对实现编程
多用组合,少用继承
模式是针对设计问题的通用解决方案,大多数模式都允许局部改变独立于其他部分。
UML
https://en.wikipedia.org/wiki/Strategy_pattern
代码
下面代码中Duck也可以设计为基类,为了简便这里简化了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <iostream> #include <memory> class FlyBehavior {public : typedef std::shared_ptr<FlyBehavior> Ptr; virtual void fly () =0 ; virtual ~FlyBehavior (){}; }; class FlyWithWings :public FlyBehavior{public : void fly () override { std::cout<<"I am flying\n" ; } }; class FlyNoWay :public FlyBehavior{public : void fly () override { std::cout<<"I can't fly\n" ; } }; class Duck {public : Duck (const FlyBehavior::Ptr &flyBehavior) : flyBehavior_ (flyBehavior) {} void performFly () {flyBehavior_->fly ();} private : FlyBehavior::Ptr flyBehavior_; }; int main () { FlyBehavior::Ptr flyBehavior (new FlyNoWay) ; Duck duck (flyBehavior) ; duck.performFly (); return 0 ; }
chap2
让你的对象知悉现况—观察者模式
定义
观察者模式 Observer Pattern
定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
分析
主题(Subject): 一般为接口或类,存储所有观察者的引用。使用一个共同的接口来更新观察者。叫talker,publisher或许更贴切;
观察者(Observer): 设置为接口,每个实体观察者需要重写接口函数。叫listener,subscriber或许更贴切;
具体观察者(Concrete
Observer): 实现观察者接口。
观察者和主题之间是松耦合:主题不知道观察者的细节,只知道观察者实现了观察者接口。
从publisher获取数据时,可以通过推push或拉pull 的方式获取数据,推似乎更常见。
有多个subscriber时,不可以依赖特定的通知顺序。
许多GUI框架经常使用观察者模式,如Android中控件的监听事件函数。这里本质是使用回调机制 实现的:publisher触发某种事件后,调用回调函数;subscriber在回调函数中实现相应自定义的功能。在调用回调函数时,可以将参数传递,也可以参数为空。
设计原则
小技巧:可以通过setChanged()
方法标记状态已经改变,然后再通知观察者,这样更有弹性,可以控制通知的时机,比如可以用来避免频繁通知观察者。
UML
https://en.wikipedia.org/wiki/Observer_pattern
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 #include <iostream> #include <memory> #include <vector> class Observer {public : typedef std::shared_ptr<Observer> Ptr; virtual void update (std::string event) =0 ; virtual ~Observer (){}; }; class ObserverOne :public Observer{public : void update (std::string event) override { std::cout<<"Observer One received response: " +event<<std::endl; } }; class ObserverTwo :public Observer{public : void update (std::string event) override { std::cout<<"Observer Two received response: " +event<<std::endl; } }; class EventSource {public : void addObserver (const Observer::Ptr& observer) {observers_.push_back (observer);} void scanInput () { std::string input; while (std::getline (std::cin,input)){ notifyObserver (input); } } private : void notifyObserver (std::string event) { for (auto item:observers_) item->update (event); } std::vector<Observer::Ptr> observers_; }; int main () { Observer::Ptr observer1 (new ObserverOne) ; Observer::Ptr observer2 (new ObserverTwo) ; EventSource eventSource; eventSource.addObserver (observer1); eventSource.addObserver (observer2); eventSource.scanInput (); return 0 ; }
chap3 装饰对象—装饰者模式
定义
装饰者模式Decorator Pattern
动态地将责任附加到对象上,若要有扩展功能,装饰者提供了比继承更有弹性的替代方案。
分析
装饰者(Decorator): 继承自组件类,包含组件引用以达到包装wrap目的,通过该引用可以调用装饰之前组件对象的方法(即转发forward),并重写一些需要修改需求的方法,以达到装饰的目的
组件(Component): 为抽象类,是具体构件和抽象装饰类的共同父类
因为装饰者必须能够取代被装饰者(组件),因此继承 在这里的作用是达到"类型匹配",而不是利用继承去获得"行为"。行为来自装饰者和基础组件,或与其他装饰者之间的组合关系。
许多Java
I/O相关类使用的就是装饰者模式,如FilterInputStream等。可以用许多装饰者包装一个组件,但这样也会造成出现许多小对象,使程序结构变得复杂。
设计原则
转发forward 与委托delegation 二者比较相近,但有区别:
下面是一个Java中例子,来自Wikipedia。Printer包含一个print方法,但是该方法自己没有具体实现,而是把责任转发给了RealPrinter的一个对象。在外界看来,好像是Printer输出了字符串,但实际是RealPrinter做了实际的工作。
划重点:转发即把锅甩给别人
应用:Chain-of-responsibility pattern、Decorator
pattern 、Proxy pattern等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class RealPrinter { void print () { System.out.println("Hello world!" ); } } class Printer { RealPrinter p = new RealPrinter(); void print () { p.print(); } } public class Main { public static void main (String[] arguments) { Printer printer = new Printer(); printer.print(); } }
委托和转发比较像,但有一些区别。委托中,self指代sender,而转发中,self指代receiver。或者,按照SO上的一个回答 ,委托算是一种特殊的转发,只不过转发给了接口本身 ,也就是策略模式中的做法。
UML
https://en.wikipedia.org/wiki/Decorator_pattern
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <iostream> #include <string> class Beverage {public : virtual double cost () =0 ; virtual ~Beverage () {} }; class Espresso :public Beverage{public : double cost () override { return 1.99 ; } }; class Mocha :public Beverage{public : Mocha (Beverage *beverage) : beverage_ (beverage) {} double cost () override { return beverage_->cost ()+.20 ; } private : Beverage* beverage_; }; int main () { Espresso coffee; std::cout<<"original coffee: " <<coffee.cost ()<<std::endl; Beverage* coffee_with_mocha=new Mocha (&coffee); coffee_with_mocha=new Mocha (coffee_with_mocha); std::cout<<"coffee with double mocha: " <<coffee_with_mocha->cost ()<<std::endl; return 0 ; }
chap 4
烘烤松耦合OO设计—工厂模式
书中分为了三种 ,即简单工厂模式、工厂方法模式和抽象工厂模式。
本节UML请参考书上
简单工厂模式
简单工厂模式其实并不算是一种设计模式,更多的时候是一种编程习惯。
定义
定义一个 工厂类,根据传入的参数不同返回不同的实例,被创建的实例具有共同的父类或接口。
分析
根据single
responsibility 原则,将对象创建工作单独分离出来一个类来做,这个类即工厂类,工厂类把全部的创建工作在一个地方处理完了。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include <iostream> #include <memory> class Shape {public : typedef std::shared_ptr<Shape> Ptr; virtual void draw () =0 ; virtual ~Shape () {} }; class Circle :public Shape{public : void draw () override { std::cout<<"draw: circle\n" ; } }; class Rectangle :public Shape{public : void draw () override { std::cout<<"draw: rectangle\n" ; } }; class ShapeFactory {public : Shape::Ptr createShape (std::string type) { Shape::Ptr shape; if (type=="circle" ) shape=Shape::Ptr (new Circle); else if (type=="rect" ) shape=Shape::Ptr (new Rectangle); return shape; } }; int main () { ShapeFactory shapeFactory; Shape::Ptr circle=shapeFactory.createShape ("circle" ); circle->draw (); Shape::Ptr rect=shapeFactory.createShape ("rect" ); rect->draw (); return 0 ; }
工厂方法模式
通常所说的工厂模式指的是这种模式,工厂方法模式是日常开发中使用频率最高的一种设计模式。
定义
定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
分析
工厂方法模式,又叫多态工厂模式。从这个名字,就可以看出其特点。简单工厂只有一个统一的工厂类,所有创建工作在一个地方都处理完了;而工厂方法是有许多工厂类,这些工厂类都实现了一个工厂基类。每一个工厂子类看起来就像简单工厂一样。
代码
下面代码中,每个工厂类对应了一个product创建,也可以对应多个product创建(这时可能需要在构造时传入参数等,像简单工厂模式那样)。注意将下面代码和简单工厂代码对比。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include <iostream> #include <memory> class Shape {public : typedef std::shared_ptr<Shape> Ptr; virtual void draw () =0 ; virtual ~Shape () {} }; class Circle :public Shape{public : void draw () override { std::cout<<"draw: circle\n" ; } }; class Rectangle :public Shape{public : void draw () override { std::cout<<"draw: rectangle\n" ; } }; class Factory {public : typedef std::shared_ptr<Factory> Ptr; virtual Shape::Ptr createShape () =0 ; virtual ~Factory () {} }; class CircleFactory :public Factory{public : Shape::Ptr createShape () override { return Shape::Ptr (new Circle); } }; class RectFactory :public Factory{public : Shape::Ptr createShape () override { return Shape::Ptr (new Rectangle); } }; int main () { Factory::Ptr factory (new CircleFactory) ; Shape::Ptr circle=factory->createShape (); circle->draw (); Factory::Ptr factory2 (new RectFactory) ; Shape::Ptr rect=factory2->createShape (); rect->draw (); return 0 ; }
抽象工厂模式
定义
提供一个接口,用于创建相关或依赖 对象的家族 ,而不需要明确指定具体类。(
在抽象工厂模式中,每一个具体工厂都提供了多个 工厂方法用于产生多种不同类型的对象)
分析
作用:把搭配的一系列对象捆绑在一起生产 ,比如下面代码中的例子。
角色:
AbstractFactory(抽象工厂) :声明了一组 用于创建对象的方法,注意是一组对象,而且这一组对象是搭配match 的;
ConcreteFactory(具体工厂) :它实现了在抽象工厂中声明的创建对象的方法,生成一组 具体对象;
AbstractProduct(抽象产品) :它为每种对象声明接口,在其中声明了对象所具有的业务方法;
ConcreteProduct(具体产品) :它定义具体工厂生产的具体对象。
抽象工厂的每个方法 实际上看像是工厂方法:每个方法都被声明为抽象,而工厂子类的方法覆盖这些方法来创建某些对象。
工厂方法每个只能创建"一个产品",而抽象工厂可以创建"一个家族产品"。
但该模式不符合开闭原则,如果加入新的产品就必须改变接口。
代码
抽象工厂模式擅长把搭配的一系列对象捆绑在一起生产,比如下面的例子。如果在生产UI控件时,把Android风格的Text控件和IOS风格的Button控件混在一起了,那将十分糟糕。而使用抽象工厂模式,可以看到可以创建相关或依赖 对象的家族 ,从而避免了把不搭的对象一起生产。youtube那个系列视频的这一节讲的很清楚,可以参考下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 #include <iostream> #include <memory> class Text {public : typedef std::shared_ptr<Text> Ptr; virtual void showText () =0 ; virtual ~Text () {} }; class Button {public : typedef std::shared_ptr<Button> Ptr; virtual void showButton () =0 ; virtual ~Button () {} }; class TextAndroid :public Text{public : void showText () override { std::cout<<"Text in Android\n" ; } }; class TextIOS :public Text{public : void showText () override { std::cout<<"Text in IOS\n" ; } }; class ButtonAndroid :public Button{public : void showButton () override { std::cout<<"Button in Android\n" ; } }; class ButtonIOS :public Button{public : void showButton () override { std::cout<<"Button in IOS\n" ; } }; class UIFactory {public : typedef std::shared_ptr<UIFactory> Ptr; virtual Text::Ptr createText () =0 ; virtual Button::Ptr createButton () =0 ; virtual ~UIFactory () {} }; class AndroidUIFactory :public UIFactory{public : Text::Ptr createText () override { return Text::Ptr (new TextAndroid); } Button::Ptr createButton () override { return Button::Ptr (new ButtonAndroid); } }; class IOSUIFactory :public UIFactory{public : Text::Ptr createText () override { return Text::Ptr (new TextIOS); } Button::Ptr createButton () override { return Button::Ptr (new ButtonIOS); } }; int main () { UIFactory::Ptr uiFactory (new AndroidUIFactory) ; Text::Ptr text=uiFactory->createText (); Button::Ptr button=uiFactory->createButton (); text->showText (); button->showButton (); return 0 ; }
chap5 独一无二的对象—单例模式
定义
单例模式Singleton Pattrn
确保一个类只有一个实例,并提供一个全局访问点。
分析
一个static变量,一个私有构造函数,一个static方法(getInstance())。
如果考虑多线程,必须进行一些改变,否则可能多个线程同时进入全局访问点中,从而创造多个对象。Java 中,改进方式有三种:
同步getInstance()方法。缺点是效率较低;
恶汉模式;
double-checked。首先检查实例是否已创建,如果尚未创建,才进行同步。
UML
https://en.wikipedia.org/wiki/Singleton_pattern
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Singleton {public : static Singleton &getInstance () { static Singleton singleton; return singleton; } Singleton (const Singleton &) = delete ; Singleton &operator =(const Singleton &) = delete ; private : Singleton () {} };
chap6 封装调用—命令模式
定义
命令模式Command Pattern
将"请求"封装成对象,以便使用不同的请求、队列或日志来参数化其他对象。命令模式也支持可撤销操作。
分析
命令Command: 具体命令类的抽象接口;
具体命令Concrete
Command: 打包运算块(一个接收者和一组动作);
接收者Receiver: 负责调用命令对象执行请求;
请求者Invoker: 以委托的形式调用命令。
一个命令对象(ConcreteCommand)通过在特定接收者 上绑定一组动作 来封装 一个请求。即命令对象将一组动作和一个接收者打包 进一个对象中,这个对象只暴露出一个execute()方法,此方法被调用时,接收者就会进行这些动作。这个打包后的对象就像一般的对象一样,可以被存储、传递和调用 。
调用者可接受命令对象当做参数,甚至在运行时动态地进行。
命令支持撤销 ,做法是实现一个undo()方法来回到execute()方法执行前的状态。
宏命令是命令的一种简单延伸,允许调用多个命令 。也支持撤销。
命令模式可以用来实现线程池、工作队列和日志请求 等。如Java中常见的开启新线程就是使用命令模式。其中Runnable接口即Command,new生成的匿名类即Concrete
Command,Thread即Invoker,Receiver省略了,具体的逻辑直接放在了Concrete
Command中。
1 2 3 4 5 6 new Thread(new Runnable(){ @Override public void run () { } }).start();
UML
https://en.wikipedia.org/wiki/Command_pattern
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include <iostream> #include <memory> class Light {public : typedef std::shared_ptr<Light> Ptr; void on () {std::cout<<"Light is on\n" ;} void off () {std::cout<<"Light is off\n" ;} }; class Command {public : typedef std::shared_ptr<Command> Ptr; virtual void execute () =0 ; virtual ~Command () {} }; class LightOnCommand :public Command{public : LightOnCommand (const Light::Ptr &light) : light_ (light) {} void execute () override { light_->on (); } private : Light::Ptr light_; }; class Remote {public : Remote () {} void setCommand (Command::Ptr command) {command_=command;} void onButtonWasPressed () {command_->execute ();} private : Command::Ptr command_; }; int main () { Light::Ptr light (new Light) ; Command::Ptr lightOn (new LightOnCommand(light)) ; Remote remote; remote.setCommand (lightOn); remote.onButtonWasPressed (); return 0 ; }
个人理解错误的地方还请不吝赐教,转载请标明出处