1 OO 设计原则
(1) 依赖倒置 DIP: 高(稳定)/低(变化) 层模块应依赖于 抽象 (稳定)
=> 面向 `接口` 编程
实现 `高内聚, 松 耦合`
依赖倒置原则.jpg
(2) 开放封闭 OCP: 对 扩展/修改 开放/封闭
=> `需求变更时, 扩展 以满足`
(3) 单一职责 SRP: 引起(类)变化 的 原因只有1个, 变化方向 隐含着(类的)责任
(4) Liskov 替换: is a 关系, 子类 可以扩展 但 不能 更改 父类功能
(5) 接口 隔离: interface 应 小而完备
(6) 优先使用 Composition (低耦合), 而不是 Inheritance(破坏 封装)
(7) 封装 变化点: 用封装 创建 对象间 分界层, 层次 间 松耦合 => 一侧修改, 不影响 另一侧
2 松耦合 设计: ptr / ref 指向 多态对象
`设计模式 中, 基本都有 Delegation`
`ptr 表达间接, 具有灵活性: 指针 可指向 obj of B hierarchy`
class A
{
B* pB;
};
设计模式 何时用
重构 -> 分离 变化点 与 稳定点 -> 变化点 用 设计模式
3 23 种 设计模式 Key Points
创建型
(1) Factory Method: 延迟 Product 类的创建到工厂子类
(2) Abstract Factory:Factory Method 上, 引入 Product 类家族
(2) Singleton 单例: 确保 类 只有 1个实例, 提供 全局访问
(4) Prototype 原型: 用 原型 instance 指定 创建对象 的 type, clone 原型 以 创建 新对象
行为型
(1) Observer 观察者: 目标(subject) 变化时, notify 其中所含 observers 去 update
(2) Template Method: NVI(non-vf 调 vf) 手法, 先构建 框架(non-vf), 将 细节(vf) 延迟 到具体子类去实现
(3) Strategy: 使 算法族 在 client 使用时 能 独立改变
结构型
(1) Decorator: 动态 扩展 功能
(2) Bridge: 解耦 抽象(is a) 与 实现(has a), 使 两者可 独立变化
(3) Adapter: 将类的 接口转换 成 client 期望的另一接口, 解决 兼容性 问题
(4) Proxy: 用代理控制 对该对象的访问
(5) Flyweight 享元: 共享 以支持大量 小尺度 对象
(6) Facade 外观: 为 子系统 中 一组接口 提供 一致外观
(7) Composite 组合: 树状结构, 使 client 可统一处理 单个对象 和 对象组合
设计模式 间 关联
(1) Factory Method
[1] 与 Template Method: new object 像 Template Method 实现算法 一样
[2] 与 Abstract Factory: 只是 没关注 class family
(2) Template Method
[1] 与 Factory Method
[2] 与 Strategy: 用 Inheritance / delegation 改变 part of 算法 / 整个算法
(3) Proxy
[1] 与 Adapter
`是否改变` 源类 `Interface` : 否 / 是
[2] 与 Decorator
1] 加以 `控制`
2] `增强` 功能
(4) Decorator / Bridge
均解决
`Inheritance` 导致的 `类膨胀 / 扩展 不灵活问题`
思路不同
2 从 C++ 到 java
相似之处
[1] ptr 在 java 中 像 C++ 中 ref
pA->data <=> (*pA).data -> a.data
[2] override
继承 vf 自然实现 / 用 @Override
[3] 虚基类 / 接口
class RealImage: public Image
class RealImage implements Image
[4] java 中 class / mem of class 均可用 public
1 Factory Method ( 2 ways ) & OOD 常用 设计思路
不用 Factory Method.png
Factory Method: 双 class 体系 .png
Factory Method: static mem func.png
创建 子类object 的标准方法
(1) 解决的问题: 解耦 client 与 ConcreteProduct
(2) 两种方法
[1] 双 class 体系
[2] static mem func // static 目的: 用 class name
(1) 不用 Factory Method
#include
// — 1. Product hierarchy
class AP // AbstractProduct
{
public:
virtual void vf() = 0;
};
class P1: public AP
{
public:
void vf() { }
};
class P2: public AP
{
public:
void vf() { }
};
// —2. client
int main()
{
std::vector
// (1) client 与 ConcreteProduct 耦合
vec.push_back(new P1);
vec.push_back(new P2);
for (int i = 0; i < vec.size(); i++)
vec[i]->vf();
}
(2) Factory Method: 双 class 体系
//—1. Product hierarchy
class AP
{
public:
virtual void vf() = 0;
};
class P1: public AP
{
public:
void vf() {}
};
class P2: public AP
{
public:
void vf() {}
};
//—2. Factory hierarchy
class AF
{
public:
virtual AP* makeP(int productId) = 0;
};
class F1 : public AF
{
public:
AP*
makeP(int productId)
{
if(productId == 1)
return new P1;
else
return new P2;
}
};
//—3. client
int main()
{
// 解耦 client 与 ConcreteProduct: 3 步曲
// 1) new ConcreteFactory, 得 AF pointer
AF* pAF = new F1;
// 2) 用 AF pointer 调 makeP(),
// 得 AP pointer
AP* pAP = pAF->makeP(1);
// 3) 用 AP pointer 调 specified mem func
pAP->vf();
}
(3) Factory Method: static mem func
#include
// —1. Product hierarchy
class AP
{
public:
static AP *makeP(int productId);
virtual void vf() = 0;
};
class P1: public AP
{
public:
void vf() { }
};
class P2: public AP
{
public:
void vf() { }
};
AP*
AP::makeP(int productId)
{
if (productId == 1)
return new P1;
else
return new P2;
}
int main()
{
std::vector
// (1) 用 class name 调 static mem func 得 AP*
vec.push_back( AP::makeP(0) );
vec.push_back( AP::makeP(1) );
// (2) 用 AP* 调 vf
for (int i = 0; i < vec.size(); i++)
vec[i]->vf();
}
2 Abstract Factory
Abstract Factory.png
在 Factory Method 上, 引入 Product 类家族
(1) 解决的问题: 多 platforms, 每 platform 多 Product, 想封装 平台的独立性
client 每次直接 new ConcreteProduct 时, 必须 区分 Platform
解决: 加 中介层 Factory hierarchy
可把 Platform 的差异性 往 上提1个层次到 Factory
只需在上一层次 Factory: new ConcreteFactory 时 区分 Platform
(1) not use Abstract Factory
// —2. client
class Client
{
public:
void vfWindow1()
{
//(1) client 直接 new ConcreteProduct
// => new ConcreteProduct 时, 就要 区分不同 Platform
#ifdef LINUX
Windows* apWindow[] =
{
new LinuxButton,
new LinuxMenu
};
#else // WINDOWS
Windows* apWindow[] =
{
new WindowsButton,
new WindowsMenu
};
#endif
apWindow[0]->vf();
apWindow[1]->vf();
}
// Butern Menu 换个顺序 显示
void vfWindow2()
{
#ifdef LINUX
Windows* apWindow[] =
{
new LinuxMenu,
new LinuxButton
};
#else // WINDOWS
Windows* apWindow[] =
{
new WindowsMenu,
new WindowsButton
};
#endif
apWindow[0]->vf();
apWindow[1]->vf();
}
};
int main()
{
Client* pClient = new Client();
pClient->vfWindow1();
pClient->vfWindow2();
}
(2) use Abstract Factory
class Client
{
private:
Factory* pFactory;
public:
Client(Factory* pFactory1) : pFactory(pFactory1) { }
// iterface to client
void vfWindow1()
{
// (1) 用 Factory Method 去 new Product
Window* apWindow[] =
{
pFactory->makeButton(),
pFactory->makeMenu()
};
apWindow[0]->vf();
apWindow[1]->vf();
}
void vfWindow2()
{
Window* apWindow[] =
{
pFactory->makeMenu(),
pFactory->makeButton()
};
apWindow[0]->vf();
apWindow[1]->vf();
}
};
int main()
{
Factory* pFactory = NULL;
// 只需 在 new ConcreteFactory 时, 区分 不同 Platform
#ifdef LINUX
pFactory = new LinuxFactory;
#else
pFactory = new WindowFactory;
#endif
Client* pClient = new Client(pFactory);
pClient->vfWindow1();
pClient->vfWindow2();
}
3 Singleton 单例: ctor 放 private 区
Singleton 类外 ( 无法创建 单例对象 ) 调 static 接口函数 A::getInstance(), 类内 创建 且 只创建 1 个 自身 static obj, return 给 外界 作 global access
(1) 饿汉: main() 单例就已产生 => 线程安全
(2) 懒汉: 第1次使用时 才构造 => 检测 + new
非线程安全: new 底层分3步 + 多线程下线程2检测到的单例指针是线程1正在构造的半构造对象->线程2访问它, undefined behavior
`new 语句 底层分解为 3 步:
分配内存 -> pointer 强转 ( 单例 obj pointer 赋值) -> ctor`
=> `单例还没构造完, pointer 可能就已赋值
(3) 懒汉 + 锁: 线程安全, 但每次获取单例时都要阻塞, 效率低
(4) 双检测 + 锁: 想提高效率, 却导致 线程不安全 (与懒汉同问题)
`检测 -> 加锁 -> 2 次检测`
(5) 智能指针类 管理 单例对象 / 资源: 懒汉 + 锁 + SP
(6) std::call_once + std::once_flag: 保证 func 只被执行 1 次 => 线程安全
(1) once_flag 不允许修改`
(2) 多线程 同时执行 func 时, 只有1个线程 active, 其余 passive`
[1] active 线程 调 func 成功完成
其余 线程 才返回`
[2] active 线程 调 func 抛出异常
从 passive 线程 中 选1个作 新 active 线程 -> 依此类推`
(7) local static obj ( 栈 ) 实现 懒汉
`C++11: local static obj` 初始化 `线程安全`
C++11 保证
local static obj: `初始化 过程 由 第1个 线程 执行完`,
`其余线程` 在此期间 要 `等待` 第1个线程 初始化完成
// 1 饿汉
#include
class Singleton
{
public:
// (5) 类外 用 类名访问 => static interface
static Singleton*
getInstance() { return ps; }
// (6) dtor 默认
~Singleton() = default;
// (7) destory
static void
destory()
{
if (ps != NULL)
{
delete ps;
ps = nullptr;
}
}
// (8) 打印 Singleton obj addr
void printAddr() const
{
std::cout << this << "n";
}
private:
// (3) private ctor + 默认 ctor => 防止 外部构造
Singleton() = default;
// (4) copy delete
Singleton& operator=(const Singleton&) = delete;
Singleton(const Singleton& singleton2) = delete;
private:
// (1) static 自身 ptr
static Singleton* ps;
};
// (2) 类外 初始化 + new -> 调 private ctor
Singleton* Singleton::ps = new Singleton;
int main()
{
Singleton* ps1 = Singleton::getInstance();
ps1->printAddr();
Singleton* ps2 = Singleton::getInstance();
ps2->printAddr();
Singleton::destory();
}
// 2 懒汉: 检测 + new
static Singleton*
getInstance()
{
if (ps == nullptr)
ps = new Singleton;
return ps;
}
// 3 懒汉 + 锁 ( => 线程安全 )
static Singleton*
getInstance()
{
std::lock_guard
if (ps == nullptr)
ps = new Singleton;
return ps;
}
// 4 双检测 + 锁 // 仅列出 dif 部分
class Singleton
{
public:
// (1) dif1
static Singleton*
getInstance()
{
if (ps == nullptr)
{
std::lock_guard
if (ps == nullptr)
ps = new Singleton;
}
return ps;
}
private:
// (2) dif2
static std::mutex mutex_;
};
// (3)dif3: static mem data: 类外 初始化
Singleton* Singleton::ps = nullptr;
std::mutex Singleton::mutex_;
#include void thread_func1() void thread_func2() int main() t1.join(); Singleton::destory(); // 5 懒汉 + 锁 + SP class Singleton if (!ups) return *ups; // 6 std::once_flag + std::call_once std::call_once( flag, return *ups; // 7 local static obj ( 栈 ) 实现 `懒汉` 4 Observer 观察者 观察者构造时, 自身(this)作实参调目标的挂接函数 attach, 将自己挂接/注册到目标对象所含观察者容器中 Observer.png Observer.png 1 解决的问题 `目标 object 状态改变` 时, `观察者` 都自动 `被 notify 并 update` 1 份 data, 多种呈现 `MVC (Model-View-Controller) 的 View 部分` 2 机制 (1) Observer -> Subject `register` Observer::Observer(Subject* pSubject) { void Subject::attach(Observer* pObs){ (2) Subject -> Observers `Push / notify` Subject 的 `notify() 定义` 在 Observer ( 的 `update() 声明` ) 定义 `之后` void (3) Observers -> Subject` `Pull: update / getVal` void Strategy.png 6 Template Method (2)解决: NVI 手法 nonvf 调 vf (3) a real example: Windows OS 打开文件 框架 Template Method.png Template Method.png 7 Decorator (2) 解决: 类层次 原/扩展 功能 Delegate 给 Decorator / Decorator 子类 实现 Decorator: 含原类层次基类指针, 以保持原功能; Decorator子类的vf override Decorator的vf, 来扩展 Decorator.png 8 Bridge (2) 解决 Bridge.png 9 Flyweight 享元 (2)对象池: 对象 data 分离 成 固有 (shared 共享 -> 避免重复创建) / 变化 部分 => 避免 重复创建 共享部分 Flyweight.png 10 Facade 外观 makeProduct / new 从 Factory Method 的 client 侧放 ProductInterface class 的 ctor 中 + store n 个 ProductPtr Facade.png 11 Proxy 代理 代理类拥有真正类的指针, 其内部 new 真正类, 外部只能看到代理类提供的接口(与 真正类同名) Proxy.png 1 解决的问题 `安全 / 分布式` 等原因 client 不可直接访问 RealSubject obj 解决: 加 中间层 ProxySubject 2 应用 [1] Windows 快捷方式 [2] 支票或银行存单 是 账户中资金 的 代理 [3] 远程代理 [4] Copy-on-Write 代理 [5] ynchronization 代理 // === C++ //—1 Interface/Subject //—2 Implement/RealSubject void vf() int main() //6 图像/Subject 第1次 从磁盘加载, 再显示 //7 图像 已加载, 后续 直接显示 // === java //—————RealImage.java private String name; @Override private void loadFromDisk(String name){ //—————ProxyImage.java private RealImage realImage; public ProxyImage(String name){ @Override //—————testProxy.java // 图像将从磁盘加载 12 Prototype 原型 `父类 不知 子类 name, 如何 创建 ?` 子类 创建自身单例, 作原型, 构造过程中将自身(this) 注册 到 父子类共用 static 原型 pointer array => 父类就能据原型 obj ptr先获取子类类型, 再 clone 出 子类 object, 父类据 Prototype.png #include enum Type class A protected: virtual Type virtual A* class A1 : public A static int objectNum; int id; //(2) private ctor // (5) // (8) int A1::objectNum = 1; int main() // (4) 用 欲 clone 的 `子类 type identifier` // (8) 13 Adapter
用吉祥物为企业赋能 | 让品牌更有趣
品牌设计/吉祥物策略/吉祥物形象设计/文创衍生品设计
电话:18026285918
ZUOART DESIGN © All Rights Reserved
#include
#include
{
Singleton* s1 = Singleton::getInstance();
s1->printAddr();
}
{
Singleton* s2 = Singleton::getInstance();
s2->printAddr();
}
{
std::thread t1(thread_func1);
std::thread t2(thread_func2);
t2.join();
}
#include
#include
{
public:
static Singleton& // def1: 返回 ref
getInstance()
{
std::lock_guard
ups.reset(new Singleton);
}
private:
static std::unique_ptr
};
std::unique_ptr
static Singleton&
getInstance()
{
static std::once_flag flag;
[&]() {
ups.reset(new Singleton);
}
);
}
static Singleton&
getInstance()
{
static Singleton intance;
return intance;
}
目标对象含1组观察者(observerVec), 其值被更改时, 自身(this)作实参(让 observer 能调它的 getVal 函数来 pull data )调观察者的 update 获取目标的值
=>
Observer::update 参数为 Subject*
=>
Observer::Ctor 参数是 Subject*
attach 参数是 Observer*
Smalltalk 中 引入了 MVC 技术, 以便 交互式 软件开发
pSubject->attach(this);
}
vec_pObs.push_back(pObs);
}
Subject::notify(){
for (int i = 0; i < vec_pObs.size(); ++i)
vec_pObs[i]->update(this);
}
Observer1::update(Subject* pSub){
std::cout << pSub->getVal() << std::endl;
}
5 Strategy: 使 算法族 在 client 使用时 能 独立改变
(1) 解决的问题: 多种算法 相似 + if...else 过多
(2) 机制
算法(策略函数对象 -> 用于 回调) / func 封装 成 class hierarchy 中 可替换的class
(1)问题: 算法 有公共部分没有复用, 公共部分变动 时, 要 维护 2 套代码
`delay 算法 变化部分` 到 `子类` 去 implement
`父类` 放一 `占位函数 vf ( 公共 interface )` 被 `non-vf 调用`
https://www.jianshu.com/p/0f209eb83f47
(1) 问题: Inheritance 导致 target 类层次 膨胀
(1) 问题: Inheritance + 多维 ( 形状 / 颜色 ) 变化 导致 类膨胀
对象 is a + has a 属性 分别 抽象成类, 分离 Inheritance 和 Delegation 使它们 独立变化, 减少 耦合
(1) 问题: 大量对象, 公共部分重复创建
#include
#include
using namespace std;
class Subject
{
public:
virtual void vf() = 0;
};
class RealSubject : public Subject
{
private:
string name;
public:
RealSubject(string name)
{
this->name = name;
loadFromDisk(name);
}
void loadFromDisk(string name){ cout << "Loading " + name << endl; }
virtual void vf() { cout << "vfing " + name << endl; }
};
//---3 ProxySubject: Inheritance + Delegation
// access to RealSubject 委托给 ProxySubject
class ProxySubject: public Subject
{
private:
RealSubject* pRealSubject; // Delegation
string name;
public:
ProxySubject(string name)
{
pRealSubject = NULL;
this->name = name;
}
{
// 4 用 ProxySubject 间接访问 RealSubject
if(pRealSubject == NULL)
{
pRealSubject = new RealSubject(name);
}
pRealSubject->vf();
}
};
{
//5 new ProxySubject
Subject* pSubject = new ProxySubject(“Image.jpg”);
pSubject->vf();
pSubject->vf();
}
//—————//Image.java
public interface Image {
void vf();
}
public class RealImage implements Image {
public RealImage(String name){
this.name = name;
loadFromDisk(name);
}
public void vf() {
System.out.println(“vfing ” + name);
}
System.out.println(“Loading ” + name);
}
}
public class ProxyImage implements Image{
private String name;
this.name = name;
}
public void vf() {
if(realImage == null){
realImage = new RealImage(name);
}
realImage.vf();
}
}
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ProxyImage(“test_10mb.jpg”);
image.vf();
System.out.println(“”);
// 图像不需要从磁盘加载
image.vf();
}
}
(1) 解决的问题: 让 父类 clone 子类 obj
using namespace std;
{
TYPE1,
TYPE2
};
{
private:
static A* prototype[10];
static int prototypeNum;
public:
static A*
findAndClone(Type type)
{
for(int k = 0; k < prototypeNum; k++)
if(prototype[k]->getType() == type)
return prototype[k]->clone();
}
virtual void operate()= 0;
// (3) attach : 可为 non-static mem
static void
attach(A* pA)
{
prototype[prototypeNum++] = pA;
}
getType() = 0;
clone() = 0;
};
A* A::prototype[];
int A::prototypeNum = 0;
{
private:
// Singleton
static A1 a1; // declaration
A1()
{
//(3) attach self_ptr to Base/Derived shared static prototype array
attach(this);
id = 0;
}
public:
// (6) clone() 调 protected ctor
A* clone() { return new A1(1); }
Type
getType() { return TYPE1; }
void operate() { cout << "clone obj id: " << id << endl; }
protected:
// (7) protected ctor
A1(int dummy){ id = objectNum++; }
};
//(1) class 外 define + init. Derived -> 调 private ctor
A1 A1::a1;
{
Type type = TYPE1;
// as arg to `call 父类 static mem findAndClone
A* pA = A::findAndClone(type);
pA->operate();
}
`deque -> stack / queue`