根据 视频 所记录的笔记以及自己的想法
向下:深入理解三大面向对象机制
- 分装:隐藏内部实现
- 继承:复用现有代码
- 多态:改写对象行为
理解隔离变化
- 从宏观层面看,面相对象的构建方式更能适应软件的变化,能将变化锁带来的影响减为最小
各司其职:
- 从微观层面来看,面相对象的方式更强调为各个类的“责任”,
- 由于需求变化导致的新增类型不应该影响原来类型的实现
对象是什么?
- 从语言事实现看,对象封装了代码和数据
- 从规格层面,对象是一些列可被使用的公共接口
- 从概念层面,对象是某种拥有责任的抽象。
面向对象设计原则
依赖倒置原则(DIP)
- 高层模块(稳定)不应该依赖于底层模块(变化),二者都应该依赖于抽象(稳定)。
- 抽象(稳定)不应该依赖于实现细节(变化),实现细节应该依赖于抽象(稳定)。
开放封闭原则(OCP)
- 对扩展开放,对更改封闭。
- 类模块应该是可扩展的,但是不可以修改。
单一职责原则(SRP)
- 一个类应该仅有一个引起它变化的原因
- 变化的方向隐含着类的责任
Liskov替换原则(LSP)
- 子类必须能够替换它们的基类(IS-A)
- 继承表达类型抽象
接口隔离原则(ISP)
- 不应该强迫客户程序(使用它的程序)依赖它们不用的方法
- 接口应该小而完备(如果子类需要用,则 public,如果子类不用,则 private)
优先使用对象组合,而不是类继承
- 类继承通常为“百箱复用”,对象组合通常为“黑箱复用”。
- 继承在某种程度上破坏了封装性,子类父类耦合度高
- 而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低
封装变化点
- 使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧进行修改,而不会对另一个侧产生不良的影响,从事实现层次间的松耦合
针对接口编程,而不是针对实现编程
- 不将变量类型声明为某个特定的具体类,而是声明为某个接口
- 客户程序无需获知对象的具体类型,只需要知道对象所具有的接口
- 减少系统中各部分的依赖关系,从而实现“高内聚,低耦合”的类型设计方案
重构关键技法
静态 -> 动态
早绑定 -> 晚绑定
继承 -> 组合
编译时依赖 -> 运行时依赖
紧耦合 -> 松耦合
“组件协作”模式
组件协作模式通过晚期绑定,来实现框架与应用程序之间的松耦合
对于某一项任务,常常有稳定的整体操作结构,但各个子步骤却又很多改变的需求,或者由于固有的原因,无法和任务的整体结构同时实现
template method、strategy、observer/ event
template method
template method:适用于 整体稳定,子部分变化
// template_Lib.cpp
class Library {
public:
// 将总体流程固定,然后部分可变,变化的留给子类去写
// 稳定的要写成非虚函数,变化的写成虚函数
// 这种情况 Run 需要是稳定的
void Run() {
Step1();
if (Step2()) { // Step2 支持变化 ==> 虚函数的多态调用
Step3();
}
for (size_t i = 0; i < 4; i++){
Step4(); // Step4 支持变化 == > 虚函数的多态调用
}
Step5();
}
virtual ~Library(); // 基类的析构函数一定要写成虚函数
// 设置成 protected ,因为仅在流程中使用才有意义,开放出去当成单独的方法没有太大意义和作用
protected:
void Step1(); // 稳定
void Step3(); // 稳定
void Step5(); // 稳定
// 纯虚函数(接口) 该类不可实例化,子类必须重写该方法
virtual bool Step2() = 0; // 变化
virtual void Step4() = 0; // 变化
};
// template_App.c
class Application : public Library {
protected:
virtual bool Step2() {
// 。。。 具体实现
}
virtual void Step4() {
// 。。。 具体实现
}
};
int main() {
Library *pLib = new Application(); // 实现多态
pLib->Run();
delete pLib;
}
strategy
定义一系列算法,把他们一个个封装起来,并且使用他们可互相替换(变化)。该模式使得算法可独立于使用他们的客户程序(稳定)而变化(扩展,子类化)
// 定义一个纯虚函数(接口),不同的子类实现了不同的变化
class TaxStrategy {
public:
virtual double Calculate(const Context& context) = 0;
virtual ~TaxStrategy() {}
};
// 子类继承这个接口,然后实现不同的纯虚方法
class CNTax :public TaxStrategy {
public:
virtual double Calculate(const Context& context) {
// ***********
}
};
class USTax :public TaxStrategy {
public:
virtual double Calculate(const Context& context) {
// ***********
}
};
class DeTax :public TaxStrategy {
public:
virtual double Calculate(const Context& context) {
// ***********
}
};
// ======================== 有变化时也不用改变 ========================
class SalesOrder {
private:
TaxStrategy *stratety; // 实现多态,根据不同的子类来调用
public:
// 使用工厂方法,决定调用哪一个子类
SalesOrder(StrategyFactory *strategyFactory) {
this->stratety = strategyFactory->NewStrategy();
}
~SalesOrder() {
delete this->stratety;
}
public double CalculateTxt() {
Context context();
double val = stratety->Calculate(context);
}
};
优点:
- strategy及其子类为组件提供了一些列可重用的算法,从而可以使得类型在运行时方便的根据需要在各个算法之间切换
- 提供了用于条件判断语句以外的另一种选择,消除条件判断语句,(其实在解耦合),含有许多条件判断语句的代码通常都需要strategy模式
- 如果strategy对象没有实例变量,那么各个上下文可以同时共享同一个strategy对象,从而节省对象开销
obsever 观察者模式
在软件构建过程中,需要为某些对象建立一种“通知依赖关系”——一个对象(目标对象)的状态发生改变,所有依赖对象(观察者对象)都将得到通知,如果这样的依赖关系过于紧密,将使软件补鞥呢很好的抵御变化
原代码
// ============================= MainForm.cpp =============================
class MainForm:public Form{
TextBox* txtFilePaht;
TextBox* txtFileNumber;
ProgressBar* progressBar;
public:
void Button1_click(){
string filePath = txtFilePath->getText();
// atoi 将字符串数字转化为 int 类型
// c_str() 获得 c 类型的字符串数组
int number = atoi(txtFileNumber->getText().c_str());
FileSplitter splitter(filePath,number);
splitter.split();
}
}
// ============================= FileSplitter.cpp =============================
class IProgress{
virtual void DoProgress(float value) = 0;
virtual ~Progress(){};
}
class FileSplitter{
string m_filePath;
int m_fileNumber;
/* 新增显示任务进度条。会产生依赖倒置,依赖了具体的实现细节,应该依赖抽象的
progressBar* 扮演通知作用,告诉外接现在的进度状况
*/
progressBar* m_progressBar // 具体的通知类型
public:
FileSplitter(const string& filePath,int fileNumber,progressBar* progressBar) :
m_filePath(file),m_fileNumber(fileNumber),m_progressBar(progressBar){
}
void split(){
// 1. 读取文件大小
// 2. 分批次向小文件中写入
for(int i = 0;i<m_fileNumber;i++){
float progressValue = (i+1)/m_fileNumber;
if(m_progressBar != nullptr){
m_progressBar->setValue(progressValue); // 更新进度条
}
}
}
}
修改后的代码(支持单个观察者)
每个类的情况都不一样,那就定义一个接口,然后类实现接口中的方法,然后在实现接口的方法中写自定义内容
不依赖于具体,而是依赖接口
// ============================= MainForm.cpp =============================
// 当有多继承,必须是一个实体类,其他继承的父类都需要是接口
class MainForm:public Form, public IPorgress {
TextBox* txtFilePaht;
TextBox* txtFileNumber;
ProgressBar* progressBar;
public:
void Button1_click(){
string filePath = txtFilePath->getText();
int number = atoi(txtFileNumber->getText().c_str());
// 此处的 this 为 MainForm 类,但因为其继承了 IProgress 接口
// 将这个类传入进去,然后 splitter 再调用 DoProgress 修改 progressBar 的值
FileSplitter splitter(filePath,number,this);
splitter.split();
}
// 实现 IProgress 接口
void DoProgress(float value){
progressBar->setValue(value);
}
}
// ============================= FileSplitter.cpp =============================
// 解耦合之后,该文件可用于各处,且不用修改代码
class IProgress{
virtual void DoProgress(float value) = 0;
virtual ~Progress(){};
}
class FileSplitter{
string m_filePath;
int m_fileNumber;
// 依赖抽象
Iprogress* m_iprogress;
public:
FileSplitter(const string& filePath,int fileNumber, Iprogress* iprogress) :
m_filePath(file),m_fileNumber(fileNumber), m_iprogress(iprogress){
}
void split(){
// 1. 读取文件大小
// 2. 分批次向小文件中写入
for(int i = 0;i<m_fileNumber;i++){
float progressValue = (i+1)/m_fileNumber;
if(m_iprogress != nullptr){
// 传递进度值
m_iporgress->DoProgress(progressValue);
}
}
}
}
支持多个观察者
// ============================= MainForm.cpp =============================
// 当有多继承,必须是一个实体类,其他继承的父类都需要是接口
class MainForm:public Form, public IPorgress {
TextBox* txtFilePaht;
TextBox* txtFileNumber;
ProgressBar* progressBar;
public:
void Button1_click(){
string filePath = txtFilePath->getText();
int number = atoi(txtFileNumber->getText().c_str());
// 此处的 this 为 MainForm 类,但因为其继承了 IProgress 接口
// 将这个类传入进去,然后 splitter 再调用 DoProgress 修改 progressBar 的值
FileSplitter splitter(filePath,number,this);
splitter.split();
}
// 实现 IProgress 接口
void DoProgress(float value){
progressBar->setValue(value);
}
}
// ============================= FileSplitter.cpp =============================
// 解耦合之后,该文件可用于各处,且不用修改代码
class IProgress{
virtual void DoProgress(float value) = 0;
virtual ~Progress(){};
}
class FileSplitter{
string m_filePath;
int m_fileNumber;
// 依赖抽象
List<Iprogress*> m_iprogressList;
public:
FileSplitter(const string& filePath,int fileNumber) :
m_filePath(file),m_fileNumber(fileNumber){
}
void split(){
// 1. 读取文件大小
// 2. 分批次向小文件中写入
for(int i = 0;i<m_fileNumber;i++){
float progressValue = (i+1)/m_fileNumber;
// 传递进度值
m_iporgress->DoProgress(progressValue);
}
// add 和 remove 表示对多个观察者的增删
void addIProgress(Iprogress* iprogress){
m_iprogressVector.push_back(iprogress);
}
void removeIProgress(Iprogress* iprogress){
m_iprogressVector.remove(iprogress);
}
protected:
virtual void onProgress(float value){
// 要遍历 list 中所有的观察者
if(m_iprogress != nullptr){
List<IProgress*>::Iterator star = m_iprogressList.begin();
m_iprogressList->DoProgress(value)
}
}
}
}
单一职责模式
在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任。
decorator 装饰模式
组合优于继承。将编译时的放到运行时
//业务操作
class Stream{
public:
virtual char Read(int number)=0;
virtual void Seek(int position)=0;
virtual void Write(char data)=0;
virtual ~Stream(){}
};
//主体类
class FileStream: public Stream{
public:
virtual char Read(int number){
//读文件流
}
virtual void Seek(int position){
//定位文件流
}
virtual void Write(char data){
//写文件流
}
};
class NetworkStream :public Stream{
public:
virtual char Read(int number){
//读网络流
}
virtual void Seek(int position){
//定位网络流
}
virtual void Write(char data){
//写网络流
}
};
class MemoryStream :public Stream{
public:
virtual char Read(int number){
//读内存流
}
virtual void Seek(int position){
//定位内存流
}
virtual void Write(char data){
//写内存流
}
};
//扩展操作
// 对文件流进行加密
class CryptoFileStream :public FileStream{
public:
virtual char Read(int number){
//额外的加密操作...
FileStream::Read(number);//读文件流
}
virtual void Seek(int position){
//额外的加密操作...
FileStream::Seek(position);//定位文件流
//额外的加密操作...
}
virtual void Write(byte data){
//额外的加密操作...
FileStream::Write(data);//写文件流
//额外的加密操作...
}
};
// 对网络流进行加密
class CryptoNetworkStream : :public NetworkStream{
public:
virtual char Read(int number){
//额外的加密操作...
NetworkStream::Read(number);//读网络流
}
virtual void Seek(int position){
//额外的加密操作...
NetworkStream::Seek(position);//定位网络流
//额外的加密操作...
}
virtual void Write(byte data){
//额外的加密操作...
NetworkStream::Write(data);//写网络流
//额外的加密操作...
}
};
// 对存储流进行加密
class CryptoMemoryStream : public MemoryStream{
public:
virtual char Read(int number){
//额外的加密操作...
MemoryStream::Read(number);//读内存流
}
virtual void Seek(int position){
//额外的加密操作...
MemoryStream::Seek(position);//定位内存流
//额外的加密操作...
}
virtual void Write(byte data){
//额外的加密操作...
MemoryStream::Write(data);//写内存流
//额外的加密操作...
}
};
class BufferedFileStream : public FileStream{
//...
};
class BufferedNetworkStream : public NetworkStream{
//...
};
class BufferedMemoryStream : public MemoryStream{
//...
}
// 对文件流既加密又缓冲
class CryptoBufferedFileStream :public FileStream{
public:
virtual char Read(int number){
//额外的加密操作...
//额外的缓冲操作...
FileStream::Read(number);//读文件流
}
virtual void Seek(int position){
//额外的加密操作...
//额外的缓冲操作...
FileStream::Seek(position);//定位文件流
//额外的加密操作...
//额外的缓冲操作...
}
virtual void Write(byte data){
//额外的加密操作...
//额外的缓冲操作...
FileStream::Write(data);//写文件流
//额外的加密操作...
//额外的缓冲操作...
}
};
void Process(){
//编译时装配
CryptoFileStream *fs1 = new CryptoFileStream();
BufferedFileStream *fs2 = new BufferedFileStream();
CryptoBufferedFileStream *fs3 =new CryptoBufferedFileStream();
}
上述代码关系如下
//业务操作
// 所有对流操作都继承与该接口
class Stream {
public:
virtual char Read(int number) = 0;
virtual void Seek(int position) = 0;
virtual void Write(char data) = 0;
virtual ~Stream() {}
};
//主体类
class FileStream : public Stream {
public:
virtual char Read(int number) {
//读文件流
}
virtual void Seek(int position) {
//定位文件流
}
virtual void Write(char data) {
//写文件流
}
};
class NetworkStream :public Stream {
public:
virtual char Read(int number) {
//读网络流
}
virtual void Seek(int position) {
//定位网络流
}
virtual void Write(char data) {
//写网络流
}
};
class MemoryStream :public Stream {
public:
virtual char Read(int number) {
//读内存流
}
virtual void Seek(int position) {
//定位内存流
}
virtual void Write(char data) {
//写内存流
}
};
//扩展操作
// 对流进行加密,可以传入不同的 Stream 子类而实现不同的功能
class CryptoStream :public Stream { // 此处的继承是为了保持成员函数签名统一
// 组合优于继承,使用组合实现该成功,而不是使用继承
Stream* stream;
public:
CryptoStream(Stream* stm) :stream(stm) {
}
virtual char Read(int number) {
//额外的加密操作...
stream->Read(number);//读文件流
}
virtual void Seek(int position) {
//额外的加密操作...
stream->Seek(position);//定位文件流
//额外的加密操作...
}
virtual void Write(byte data) {
//额外的加密操作...
stream->Write(data);//写文件流
//额外的加密操作...
}
};
class BufferedStream : public Stream {
// 组合优于继承,使用组合实现该成功,而不是使用继承
Stream* stream;
public:
BufferedStream(Stream* stm) :stream(stm) {
}
virtual char Read(int number) {
//额外的缓冲操作...
stream->Read(number);//读流
}
virtual void Seek(int position) {
//额外的缓冲操作...
stream->Seek(position);//定位流
//额外的缓冲操作...
}
virtual void Write(byte data) {
//额外的缓冲操作...
stream->Write(data);//写流
//额外的缓冲操作...
}
};
void Process() {
//运行时加载
FileStream* s1 = new FileStream();
// 对文件流进行加密
CryptoStream* s2 = new CryptoStream(s1);
// 对文件流既加密又缓冲
BufferedStream* s3 = new BufferedStream(s2);
// 对文件流既加密又缓冲
s3->Read();
}
自己理解后仿制写的
#include<iostream>
#include<string>
class Stream {
public:
virtual void Read() = 0;
};
class FileStream :public Stream {
public:
virtual void Read() {
std::cout << "执行文件流操作" << std::endl;
}
};
class CryptoStream :public Stream {
Stream* m_stream;
public:
CryptoStream(Stream* stream) :m_stream(stream) {
}
virtual void Read() {
std::cout << "执行加密操作" << std::endl;
m_stream->Read();
}
};
class BufferStream :public Stream {
Stream* m_stream;
public:
BufferStream(Stream* stream) :m_stream(stream) {
}
virtual void Read() {
std::cout << "执行缓冲操作" << std::endl;
m_stream->Read();
}
};
void propress() {
FileStream* stream = new FileStream();
CryptoStream* crypStream = new CryptoStream(stream);
BufferStream* bufferStream = new BufferStream(crypStream);
bufferStream->Read();
std::string str;
std::cin >> str;
}
上一个版本中大部分子类都有 Stream* 变量,但是 FileStream、NetworkStream、MemoryStream 类中不需要 Stream* 变量,可以在中间加上一个类 Decorate,该类继承,该类中有 Stream* 变量 ,然后让 CryptoStream、 BufferedStream 类继承 中间类Decorate
#include <windows.h>
//业务操作
class Stream {
public:
virtual char Read(int number) = 0;
virtual void Seek(int position) = 0;
virtual void Write(char data) = 0;
virtual ~Stream() {}
};
//主体类
class FileStream : public Stream {
public:
virtual char Read(int number) {
//读文件流
}
virtual void Seek(int position) {
//定位文件流
}
virtual void Write(char data) {
//写文件流
}
};
class NetworkStream :public Stream {
public:
virtual char Read(int number) {
//读网络流
}
virtual void Seek(int position) {
//定位网络流
}
virtual void Write(char data) {
//写网络流
}
};
class MemoryStream :public Stream {
public:
virtual char Read(int number) {
//读内存流
}
virtual void Seek(int position) {
//定位内存流
}
virtual void Write(char data) {
//写内存流
}
};
//扩展操作
class Decorate :public Stream {
Stream* m_stream; // 用组合的方式来实现多态
public:
Decorate(Stream* stream) :m_stream(stream) {
}
};
// 对流进行加密,可以传入不同的 Stream 子类而实现不同的功能
class CryptoStream :public Decorate {
// 组合优于继承,使用组合实现该成功,而不是使用继承
Stream* stream;
public:
CryptoStream(Stream* stm) :Decorate(stm) {
}
virtual char Read(int number) {
//额外的加密操作...
stream->Read(number);//读文件流
}
virtual void Seek(int position) {
//额外的加密操作...
stream->Seek(position);//定位文件流
//额外的加密操作...
}
virtual void Write(byte data) {
//额外的加密操作...
stream->Write(data);//写文件流
//额外的加密操作...
}
};
class BufferedStream : public Decorate {
// 组合优于继承,使用组合实现该成功,而不是使用继承
Stream* stream;
public:
BufferedStream(Stream* stm) :Decorate(stm) {
}
virtual char Read(int number) {
//额外的缓冲操作...
stream->Read(number);//读流
}
virtual void Seek(int position) {
//额外的缓冲操作...
stream->Seek(position);//定位流
//额外的缓冲操作...
}
virtual void Write(byte data) {
//额外的缓冲操作...
stream->Write(data);//写流
//额外的缓冲操作...
}
};
void Process() {
//运行时加载
FileStream* s1 = new FileStream();
// 对文件流进行加密
CryptoStream* s2 = new CryptoStream(s1);
// 对文件流既加密又缓冲
BufferedStream* s3 = new BufferedStream(s2);
// 对文件流既加密又缓冲
s3->Read();
}
上述代码结构如下
代码中的继承是为了接口的规范,组合是为了实现更多的扩展
动机 (Motivation)
- 在某些情况下我们可能会“过度地使用继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。
- 如何使"对象功能的扩展"能够根据需要来动态地实现?同时避免“扩展功能的增多"带来的子类膨胀问题?从而使得任何“功能扩展变化"所导致的影响将为最低?
动态(组合)地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码&减少子类个数)
总结:
通过采用组合而非继承的手法,Decorator模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了使用继承带来的“灵活性差”和“多子类衍生问题”。
Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-aComponent的组合关系,即Decorator类又使用了另外-个Component类。
Decorator模式的目的并非解决“多子类衍生的多继承"问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。
bridge 桥模式
动机 (Motivation)
由于某些类型的固有的实现逻辑,使得它们具有两个变化的维度,乃至多个纬度的变化。
如何应对这种“多维度的变化"?如何利用面向对象技术来使得类型可以轻松地沿着两个乃至多个方向变化,而不引[入额外的复杂度?
做法
将抽象部分(业务功能)与实现部分(平台实现)分离,使它们都可以独立地变化。
原代码
class Messager{
public:
virtual void Login(string username, string password)=0;
virtual void SendMessage(string message)=0;
virtual void SendPicture(Image image)=0;
virtual void PlaySound()=0;
virtual void DrawShape()=0;
virtual void WriteText()=0;
virtual void Connect()=0;
virtual ~Messager(){}
};
//平台实现
class PCMessagerBase : public Messager{
public:
virtual void PlaySound(){
//**********
}
virtual void DrawShape(){
//**********
}
virtual void WriteText(){
//**********
}
virtual void Connect(){
//**********
}
};
class MobileMessagerBase : public Messager{
public:
virtual void PlaySound(){
//==========
}
virtual void DrawShape(){
//==========
}
virtual void WriteText(){
//==========
}
virtual void Connect(){
//==========
}
};
//业务抽象
class PCMessagerLite : public PCMessagerBase {
public:
virtual void Login(string username, string password){
PCMessagerBase::Connect();
//........
}
virtual void SendMessage(string message){
PCMessagerBase::WriteText();
//........
}
virtual void SendPicture(Image image){
PCMessagerBase::DrawShape();
//........
}
};
class PCMessagerPerfect : public PCMessagerBase {
public:
virtual void Login(string username, string password){
PCMessagerBase::PlaySound();
//********
PCMessagerBase::Connect();
//........
}
virtual void SendMessage(string message){
PCMessagerBase::PlaySound();
//********
PCMessagerBase::WriteText();
//........
}
virtual void SendPicture(Image image){
PCMessagerBase::PlaySound();
//********
PCMessagerBase::DrawShape();
//........
}
};
class MobileMessagerLite : public MobileMessagerBase {
public:
virtual void Login(string username, string password){
MobileMessagerBase::Connect();
//........
}
virtual void SendMessage(string message){
MobileMessagerBase::WriteText();
//........
}
virtual void SendPicture(Image image){
MobileMessagerBase::DrawShape();
//........
}
};
class MobileMessagerPerfect : public MobileMessagerBase {
public:
virtual void Login(string username, string password){
MobileMessagerBase::PlaySound();
//********
MobileMessagerBase::Connect();
//........
}
virtual void SendMessage(string message){
MobileMessagerBase::PlaySound();
//********
MobileMessagerBase::WriteText();
//........
}
virtual void SendPicture(Image image){
MobileMessagerBase::PlaySound();
//********
MobileMessagerBase::DrawShape();
//........
}
};
void Process(){
//编译时装配
Messager *m =
new MobileMessagerPerfect();
}
按照装饰器模式,将继承改成组合,用指针实现多态
class Messager {
public:
virtual void Login(string username, string password) = 0;
virtual void SendMessage(string message) = 0;
virtual void SendPicture(Image image) = 0;
virtual void PlaySound() = 0;
virtual void DrawShape() = 0;
virtual void WriteText() = 0;
virtual void Connect() = 0;
virtual ~Messager() {}
};
//平台实现
class PCMessagerBase : public Messager {
public:
virtual void PlaySound() {
//**********
}
virtual void DrawShape() {
//**********
}
virtual void WriteText() {
//**********
}
virtual void Connect() {
//**********
}
};
class MobileMessagerBase : public Messager {
public:
virtual void PlaySound() {
//==========
}
virtual void DrawShape() {
//==========
}
virtual void WriteText() {
//==========
}
virtual void Connect() {
//==========
}
};
//业务抽象
class MessagerLite : public Messager{
/* new PCMessagerBase 或者 MobileMessagerBase
但 PCMessagerBase MobileMessagerBase 是纯虚函数 ,无法实例化
发现 Messager 中两波函数不属于一个东西,应该拆分
*/
Messager* message; // new PCMessagerBase 或者 MobileMessagerBase
public:
virtual void Login(string username, string password) {
message->Connect();
//........
}
virtual void SendMessage(string message) {
message->WriteText();
//........
}
virtual void SendPicture(Image image) {
message->DrawShape();
//........
}
};
class MessagerPerfect : public Messager {
Messager* message;
public:
virtual void Login(string username, string password) {
message->PlaySound();
//********
message->Connect();
//........
}
virtual void SendMessage(string message) {
message->PlaySound();
//********
message->WriteText();
//........
}
virtual void SendPicture(Image image) {
message->PlaySound();
//********
message->DrawShape();
//........
}
};
void Process() {
//编译时装配
Messager *m =
new MobileMessagerPerfect();
}
最终修改后的代码
class Messager {
protected:
// 所有子类中都有的,可以往父类中提
MessagerImplement* m_messageImp; // 父类中加一个构造函数初始化该字段
public:
Messager(MessagerImplement* messageImp) :m_messageImp(messageImp) {
}
virtual void Login(string username, string password) = 0;
virtual void SendMessage(string message) = 0;
virtual void SendPicture(Image image) = 0;
virtual ~Messager() {}
};
class MessagerImplement {
public:
virtual void PlaySound() = 0;
virtual void DrawShape() = 0;
virtual void WriteText() = 0;
virtual void Connect() = 0;
virtual ~MessagerImplement() {}
};
//平台实现
class PCMessagerImp : public MessagerImplement {
public:
virtual void PlaySound() {
//**********
}
virtual void DrawShape() {
//**********
}
virtual void WriteText() {
//**********
}
virtual void Connect() {
//**********
}
};
class MobileMessagerImp : public MessagerImplement {
public:
virtual void PlaySound() {
//==========
}
virtual void DrawShape() {
//==========
}
virtual void WriteText() {
//==========
}
virtual void Connect() {
//==========
}
};
//业务抽象
class MessagerLite : public Messager{ // 此处的继承是为了保证接口同一
// new PCMessagerBase 或者 MobileMessagerBase
// 但 PCMessagerBase MobileMessagerBase 是纯虚函数 ,无法实例化(因为Message 总有的纯虚函数没有重写
// 发现 Messager 中两波函数不属于一个东西,应该拆分
//MessagerImplement* messageImp;
// 所有子类中都有的,可以往父类中提
//MessagerImplement* messageImp;
public:
MessagerLite(MessagerImplement* messageImp) :Messager(messageImp) {
}
virtual void Login(string username, string password) {
m_messageImp->Connect();
//........
}
virtual void SendMessage(string message) {
m_messageImp->WriteText();
//........
}
virtual void SendPicture(Image image) {
m_messageImp->DrawShape();
//........
}
};
class MessagerPerfect : public Messager {
// 所有子类中都有的,可以往父类中提
//MessagerImplement* messageImp;
public:
MessagerPerfect(MessagerImplement* messageImp) :Messager(messageImp) {
}
virtual void Login(string username, string password) {
m_messageImp->PlaySound();
//********
m_messageImp->Connect();
//........
}
virtual void SendMessage(string message) {
m_messageImp->PlaySound();
//********
m_messageImp->WriteText();
//........
}
virtual void SendPicture(Image image) {
m_messageImp->PlaySound();
//********
m_messageImp->DrawShape();
//........
}
};
void Process() {
// 运行时装配
PCMessagerImp* pcMessagerImp = new PCMessagerImp();
MessagerLite* messagerLite = new MessagerLite(pcMessagerImp);
}
要点总结
Bridge模式使用"对象间的组合关系"解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自纬度的变化,即“子类化"它们。
Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法。
Bridge模式的应用一般在“两个非常强的变化维度”,有时一个类也有多于两个的变化维度,这时可以使用Bridge的扩展模式
"对象创建"模式
通过“对象创建”模式绕开 new ,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。
他是接口抽象之后的第一步工作
典型模式:
- Factory Method
- Abstract Factory
- Prototype
- Builder
Factory Method 工厂模式
定义一个用于创建对象的接口,让子类决定实例化哪一个类FactoryMethod使得一个类的实例化延迟(目的:解耦,手段:虚函数)到子类。
void SomeMethod(){
// ...
// 此行代码是具体的实现,后期如果有变化,会导致依赖倒置,因此声明接口
BianrySplitter* bianrySplitter = new BianrySplitter();
// ...
}
声明接口
// 声明接口
class ISplitter {
virtual void split() = 0;
virtual ~ISplitter(){}
};
class BianrySplitter :public ISplitter {
};
class TextSplitter :public ISplitter {
};
class VedioySplitter :public ISplitter {
};
void SomeMethod(){
// ...
// ISplitter* splitter 编程抽象,但 new BianrySplitter() 还是具体实现,可通过 工厂模式 避开 new 紧耦合
ISplitter* splitter = new BianrySplitter();
// ...
}
将依赖导出到外部去,下面代码中的依赖和 SomeClass 类没有关系了
// 声明接口
class ISplitter {
virtual void split() = 0;
virtual ~ISplitter() {}
};
class BianrySplitter :public ISplitter {
virtual void split() {}
};
class TextSplitter :public ISplitter {
virtual void split() {}
};
class VedioySplitter :public ISplitter {
virtual void split() {}
};
// 声明一个工厂基类
class SplitterFactory {
public:
virtual ISplitter* CreateSplitter() = 0;
virtual ~SplitterFactory() {}
};
// 具体工厂
// 每一个类都有一个具体的工厂,向创建某个类,可直接调用某个具体工厂
class BinarySplitterFactory : public SplitterFactory {
public:
virtual ISplitter* CreateSplitter() {
return new BianrySplitter();
}
//virtual ~BinarySplitterFactory() {}
};
class TextSplitterFactory : public SplitterFactory {
public:
virtual ISplitter* CreateSplitter() {
return new TextSplitter();
}
//virtual ~TextSplitterFactory() {}
};
class VedioSplitterFactory : public SplitterFactory {
public:
virtual ISplitter* CreateSplitter() {
return new VedioySplitter();
}
//virtual ~VedioSplitterFactory() {}
};
// 将类中具体实现放到外面去,这样类可以更加灵活
class SomeClass {
SplitterFactory* m_factor;
public:
SomeClass(SplitterFactory* factor) :m_factor(factor) {
}
void SomeMethod() {
// ...
// 将变化交给 SplitterFactory
ISplitter* splitter = m_factor->CreateSplitter();
// ...
}
};
int main() {
// 将变化交到外面
SplitterFactory* binary = new BinarySplitterFactory();
SomeClass* c1 = new SomeClass(binary);
c1->SomeMethod();
return 0;
}
总结:
Factory Method模式用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合关系(new)会导致软件的脆弱。
Factory Method模式通过面向对象的手法,将所要创建的具体对象工作延迟到子类,从而实现一种扩展(而非更改)的策略,较好地解决了这种紧耦合关系。
Factory Method模式解决“单个对象"的需求变化。缺点在于要求创建方法/参数相同。