读书笔记-设计模式
目录
设计模式的几大原则
1、开闭原则(Open Close Principle)
开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、里氏代换原则(Liskov Substitution Principle)
里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
3、依赖倒转原则(Dependence Inversion Principle)
这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。
5、迪米特法则,又称最少知道原则(Demeter Principle)
最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则(Composite Reuse Principle)
合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。
7、单一职责原则(Single Responsibility Principle)
一个类只负责一个功能领域的相应的职责。
创建型-工厂和抽象工厂模式
普通工厂模式:
抽象工厂模式:
水果类和苹果类,橙子类 和动物类一致,所以未在单独定义。
单例模式
单例看似简单,但是要写好单例,并非将构造函数设置为private,然后通过getInstance()将类中创建好的static实例暴露出去那么简单。这里面需要注意线程安全问题。
C++版本线程安全示例:
#ifndef __SINGLETON_H__
#define __SINGLETON_H__
#include <pthread.h>
//#include <sys/types.h>
/*
线程安全的单例模式
*/
#define SINGLETON(__CLASS) container::CSingleton<__CLASS>::Instance()
namespace container
{
template <typename T>
class CSingleton
{
public:
static T * Instance()
{
if ( !m_pInstance )
{
++m_pInstance; //防止优化
--m_pInstance;
pthread_mutex_lock(&m_mutex) ;
if ( !m_pInstance )
{
if ( m_destroyed )
{
OnDeadReference();
} else
{
Create();
}
}
pthread_mutex_unlock(&m_mutex);
}
return m_pInstance;
}
private:
static T * m_pInstance;
static bool m_destroyed;
static pthread_mutex_t m_mutex;
private:
CSingleton();
CSingleton(const CSingleton&);
CSingleton& operator=(const CSingleton&);
~CSingleton()
{
m_pInstance = 0;
m_destroyed = true;
}
static void Create()
{
static T sInstance;
m_pInstance = &sInstance;
}
static void OnDeadReference()
{
Create();
new (m_pInstance) T;
atexit(KillPhoenixSingleton);
m_destroyed = false;
}
static void KillPhoenixSingleton()
{
m_pInstance->~T();
}
};
template <typename T>
T * CSingleton<T>::m_pInstance = NULL;
template <typename T>
bool CSingleton<T>::m_destroyed = false;
template <typename T>
pthread_mutex_t CSingleton<T>::m_mutex = PTHREAD_MUTEX_INITIALIZER;
}
#endif
创建者模式(builder)
builder 模式在成产品过程较为复杂,而且这些产品具有一定共性的情况下比较适合。 例如在生成某个对象时需要执行很多操作。否则就不一定适用。 切记不要为了适用设计模式而适用设计模式。
Builder类定义生成对象的操作步骤。Builder可以定义抽象类,然后不同产品去继承它并且实现个性化的部分。
Director类负责组织生成对象的过程。
Facade Pattern外观者模式
外观模式定义:为子系统中的一组接口提供一个一致的界面
此模式定义了一个高层接口,该接口使得这一子系统更加容易使用
例如,一个人在理财时有投资和抛售两种行为。
投资和抛售的对象有可能是股票,基金等。
public class person()
{
public void invest()
{
stock apple = new stock();
stock google = new stock();
currency rmb = new currency();
currency dollar = new currency();
apple.buy();
google.buy();
rmb.buy();
dollar.buy();
}
public void disinvest()
{
new xxx();
xxx.sell();
...
}
}
但是实际上用户只关注是投资还是抛售,不关心这个具体内容。所以可以把投资的内容和抛售的内容抽象出来。
public class invest()
{
public buy()
{
stock apple = new stock();
stock google = new stock();
currency rmb = new currency();
currency dollar = new currency();
apple.buy();
google.buy();
rmb.buy();
dollar.buy();
}
}
public class disinvest()
{
public sell()
{
stock apple = new stock();
stock google = new stock();
currency rmb = new currency();
currency dollar = new currency();
apple.sell();
google.sell();
rmb.sell();
dollar.sell();
}
}
public class person()
{
public void invest()
{
invest i = new invest();
i.buy();
}
public void disinvest()
{
disinvest d = new disinvest();
d.sell();
}
}
装饰器decorater pattern
装饰器允许对现有的对象增加新的功能,但是又不改变当前对象。
举个例子:
咖啡,从种类上来分有a,b两种, 每种咖啡可以加不同的调味品,其中调味品目前有两种,c和d。
那么应该怎么样合理地写一套代码去计算某种咖啡在加不同调味品时的最终价格呢?
如果定义coffee接口,包含cost函数,若分别写四个类ac,ad,bc,bd去实现coffee接口重写cost类,一来类比较多,以后新增咖啡类型和调味品的时候,之类数量会急剧飙升。另外也不利于扩展,因为有人可能会点一杯咖啡陪两份不同的调味品。
需要引入装饰器设计模式。
它由几个重要的部分组成。
1,抽象组件类。 即案例中的咖啡类。
2,具体组件类。即案例中的a和b。
3,装饰器类。这里可以多抽象一成公共的装饰器,然后具体的装饰元素再继承自这个公共的装饰器。即c和d。
构建装饰器模式有几点公式:
1,具体组件类和装饰器类都继承自抽象组件类。并且装饰器类拥有抽象组件类的引用。
2,可以结合装饰着类和具体组件类创建出新的类。例如ac,ad。
3,公式2可以重复执行,直到创建出符合需求的类为止。
使用的时候一般都是new装饰器,然后装饰器构造函数的入场是其它装饰器或者具体组件类。
public interface coffee()
{
public double cost();
}
public class coffeeA implements coffee()
{
public double cost()
{
return 1.0;
}
}
public class coffeeB implements coffee()
{
public double cost()
{
return 2.0;
}
}
public class Decorate implements coffee()
{
coffee co;
public Decorate(cooffee co)
{
this.co = co;
}
public double cost()
{
return co.cost();
}
}
public class DecorateC extends Decorate()
{
public DecorateC(coffee co)
{
super(co);
}
public double cost()
{
super.cost() + 0.1;
}
}
public class DecorateD extends Decorate()
{
public DecorateC(coffee co)
{
super(co);
}
public double cost()
{
super.cost() + 0.2;
}
}
测试程序:
public static void main(argv[]s)
{
//一杯咖啡A增加了C D两种调味料
coffee co = new DecorateC(new DecorateD(new CoffeeA()));
co.cost();
}
adaptor模式
作为两个互不兼容的接口之前的桥梁。 它的目的是将现有的某个接口转换成符合使用者预期的接口。
例如中国的插座提供sup220v接口,美国的插座提供sup110v接口,如果要向美国充电器能在中国插座上实用,那必须提供一个adaptor将中国插座转换成符合美国充电器的格式。
或者统一提供一个adaptor对所有国家的充电器都进行兼容,对外提供统一的充电api。让充电者不用管内部实现,只调用统一的充电接口即可。
public interface ICharge()
{
public void charge(string type);
}
public class UsCharge implements ICharge()
{
public void charge(string type)
{
System.out.println("i'm in type us");
}
}
public interface IOtherCharge()
{
public void otherCharge(string type);
}
public class ChinaCharge implements IOtherCharge()
{
public void otherCharge(string type)
{
System.out.println("i'm in type" + type);
}
}
public class ChargeAdaptor implements ICharge()
{
public void charge(string type)
{
if (type == "china")
{
ChinaCharge cc = new ChinaCharge("china");
cc.otherCharge();
}
else if (type == "us")
{
UsCharge uc = new UsCharge("us);
uc.charge();
}
}
}
public class Charge()
{
public static void main(string[] args)
{
ChargeAdaptor ca = new ChargeAdaptor();
ca.charge("china");
ca.charge("us");
}
}