类设计考虑的问题
1.类的组织与表示
聚类分析、类的再抽象、类的拆分、类的复用性... ...
2.行为的组织与表示
行为的分解、行为的参与者、行为的分组与接口、行为的差异... ...
3.属性的组织与表示
复合属性、只读只写属性、不变属性、运行时属性... ...
变化与复用
1.变化
职责的变化(接口、功能)
实现的变化(数据表示、行为)
2.变化的适应方式
扩展既有代码:继承、关联、依赖、聚合、组合
直接修改代码的问题:获取既有代码、改变实现的顾虑、改变接口的顾虑
设计模式基本原则
抽象与封装原则:
分离稳定与变化
封装变化
抽象变化接口
针对接口编程而不针对实现编程
组合优先原则(而不是继承)
类的关系
class A{
public:
void f();
}
关联
class B{
public:
void g();
private:
A*pa;
}
依赖(实例级关联)
class B{
public:
void g(A&a){ a.f()};
}
组合/聚合(类级关联)
class B{
public:
void g();
private:
A*pA;
}
泛化关系
实现关系
创建型设计模式-工厂方法模式
动机:
顾客Client类与产品耦合紧密,
产品经常变化(增加,添加属性)
class Client(){
void f(){
Dog*d=new Dog;
}
}
//使用IAnimal接口
class Client(){
void f(){
IAnimal*ani=new Dog;
}
}
//工厂方法1-分离实例化部分
class Client(){
void f(){
IAnimal*ani=CreateAnimal();
}
virtual IAnimal*CreateAnimal();//工厂方法
}
//工厂方法2-分离实例化部分
class Client(){
void f(Factory&fac){
IAnimal*ani=fac.CreateAnimal();
}
}
//独立出工厂
class Factory{
virtual IAnimal*CreateAnimal();
}
总结
定义顶层工厂接口,由接口决定产品的创建
优点:
扩展产品支持开闭原则(扩展开发,修改关闭):新增产品树、类;用同构产品树替换原产品树
不足:
继承导致:子类数量过多
创建型设计模式-简单工厂(静态工厂)方法模式
和工厂方法的区别:
如果产品种类稳定,则从Factory派生子类失去意义,所以Factory中的方法成为类方法将更加合理
在实例化对象时,如果输入条件(参数)不一样,产生的对象也不一样,那么我们可以考虑使用简单工厂对不同的实例进行统一封装。
创建型设计模式-单件模式
动机:
仅有一个实例或至多有N个实例
能够从外部访问它
即程序在运行的过程中,希望在任意时刻,都只保留某个对象的唯一实例
class single{
public:
static single*instance();
void f();
private:
single(){...}
single(const single&);//禁用
single&operator=(const single&);
private:
static single*instance;
}
single*single::instance=0;
single*single::instance(){
if(instance==0){
instance=new instance;
}
return instance;
}
创建型设计模式-构造器/生成器模式
动机:
创建复杂对象
陈老师的创建墙的例子:
当创建墙的时候,需要创建水泥和砖
采用工厂方法去创建,工厂方法里面包括了:创建水泥和创建砖方法
通过调用
factory.创建水泥();
factory.创建砖();
factory.创建水泥();
factory.创建砖();
创建墙
问题:在创建墙的过程中,水泥和砖的创建次数是不需要知道的
墙构造器.创建砖(5){
循环5次factory.创建砖();
}
墙构造器.创建水泥(5){
循环5次factory.创建水泥();
}
如果需要适应更多的变化,比如构造一层砖,则可以再抽象 出一个类
在创建时,先生成一个墙的Builder类,然后在Director指示器的方法中,选择要创建的行为,该行为调用Builder中的创建方法,最后返回创建完毕的墙。
使用相同的产品但使用不同的构造器,可以产生不同的复杂对象。
构造器隐藏了构建的细节和装配的过程
创建型设计模式-原型方法
动机:
当我们需要创建多次/多个,相同的元素时。
我们在程序运行过程中,当需要有新的实例对象(但是状态基本相同的元素)时使用。
例子:我的世界中,草坪块,变成有积雪覆盖的草坪块。
这个时候就需要一个新的图块对象覆盖原本的块。
不同的解决方案:
1.使用构造函数
属于硬编码,不能动态改变创建的类
2.拷贝/构造函数
new 沙块(a沙块);
new 岩石(a岩石);
需要知道确切的类型
原型方法:
class BlockManager(){
private:
static Sand* aSand;
static Rock* aRock;
public:
Block*Create(int id){
if(id==1){return aSand.clone();}
else if(id==2){return aRock.clone();}
}
}
原型方法可以有效减少子类的数量,但是事先clone接口可能存在难度。
创建型设计模式-抽象工厂方法
动机:
如上图,当需要限制,喜欢猫的小孩只能创建猫。
限制用户使用同一系列的产品。
(window操作系统下面,只能使用window下面的鼠标、窗口事件)