IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 设计模式:面向对象的设计原则 -> 正文阅读

[C++知识库]设计模式:面向对象的设计原则

问题引入:为什么要使用面向对象的设计?

面向对象设计最大的优势在于抵御变化,变化是复用的天敌!

如何解决复杂性?

  • 分解
    人们面对复杂性有一个常见的做法:即分而治之,将大问题分解成许多的小问题,将复杂问题分解成多个简单问题

  • 抽象
    更高层次来讲,人们处理复杂性有一个通用的技术,即抽象。由于不能掌握全部的复杂对象,我们选择忽视它的非本质细节,而去处理泛化和理想化的对象模型。

下面通过代码来理解分解和抽象:

  • 分解的做法:
class Point {
public:
	int x;
	int y;
};

class Line {
public:
	Point start;
	Point end;
	Line(const Point& start,const Point& end) {
		this->start = start;
		this->end = end;
	}
};

class Rect {
public:
	Point leftUp;
	int width;
	int height;
	Rect(const Point& leftUp,int width,int height) {
		this->leftUp = leftUp;
		this->width = width;
		this->height = height;
	}	
};


// MainForm
class MainForm : public Form {
private:
	Point p1;
	Point p2;
	// 分而治之
	vector<Line> lineVector; // 画线
	vector<Rect> rectVector; // 画矩形
	// ... 扩展:画圆等
public:
	MainForm(){// ...}
protected:
virtual void OnMouseDown(const MouseEventArgs& e);
virtual void OnMouseUp(const MouseEventArgs& e);
virtual void OnPaint(const PaintEventArgs& e);
};

void MainForm::OnMouseDown(const MouseEventArgs& e) {
	p1.x = e.X;
	p1.y = e.Y;
	// ...
	Form::OnMouseDown(e);
}

void MainForm::OnMouseUp(const MouseEventArgs& e) {
	p2.x = e.X;
	p2.y = e.Y;
	
	if(rdoLine.Checked) {
		Line line(p1,p2);
		lineVector.push_back(line);
	}
	else if(rdoRect.Checked) {
		int width = abs(p2.x - p1.x);
		int height = abs(p2.y - p1.y);
		Rect rect(p1, width, height);
		rectVector.push_back(rect);
	}

	// ...
	this->Refresh(); // 系统会调用OnPaint
	Form::OnMouseUp(e);
}

void MainForm::OnPaint(const MouseEventArgs& e) {
	// 针对直线
	for(int i = 0; i < lineVector.size(); i++) {
		e.Graphics.DrawLine(Pens.Red,
		lineVector[i].start.x,
		lineVector[i].start.y,
		lineVector[i].end.x,
		lineVector[i].end.y)
	}

	// 针对矩形
	for(int i = 0; i < rectVector.size(); i++) {
		e.Graphics.DrawRect(Pens.Red,
		rectVector[i].leftUp,
		rectVector[i].width,
		rectVector[i].height);
	}
	// ...
	Form::OnPaint(e);
}
  • 抽象的做法
class Shape {
	virtual Doraw(const Graphics& g) = 0;
	virtual ~Shape(){}
}
class Point {
public:
	int x;
	int y;
};

class Line : public Shape {
public:
	Point start;
	Point end;
	Line(const Point& start,const Point& end) {
		this->start = start;
		this->end = end;
	}
	// 实现自己的Draw,负责画自己
	virtual void Draw(const Graphics& g) {
		g.DrawLine(Pen.Red,
			start.x, start.y, end.x, end.y);
	}
};

class Rect : public Shape{
public:
	Point leftUp;
	int width;
	int height;
	Rect(const Point& leftUp,int width,int height) {
		this->leftUp = leftUp;
		this->width = width;
		this->height = height;
	}
	// 实现自己的Draw,负责画自己
	virtual void Draw(const Graphics& g) {
		g.DrawRect(Pen.Red,
			leftUp, width, height);
	}
};
// MainForm
class MainForm : public Form {
private:
	Point p1;
	Point p2;
	// 针对所有形状
	vector<Shape*> shapeVector; // 依赖抽象,使用指针,可以实现多态,如果不使用指针,会对对象进行切割
public:
	MainForm(){// ...}
protected:
virtual void OnMouseDown(const MouseEventArgs& e);
virtual void OnMouseUp(const MouseEventArgs& e);
virtual void OnPaint(const PaintEventArgs& e);
};

void MainForm::OnMouseDown(const MouseEventArgs& e) {
	p1.x = e.X;
	p1.y = e.Y;
	// ...
	Form::OnMouseDown(e);
}

void MainForm::OnMouseUp(const MouseEventArgs& e) {
	p2.x = e.X;
	p2.y = e.Y;
	
	if(rdoLine.Checked) {
		shapeVector.push_back(new Line(p1,p2));
	}
	else if(rdoRect.Checked) {
		int width = abs(p2.x - p1.x);
		int height = abs(p2.y - p1.y);
		shapeVector.push_back(new Rect(p1,width,height));
	}
	// 扩展
	// ...
	this->Refresh(); // 系统会调用OnPaint
	Form::OnMouseUp(e);
}

void MainForm::OnPaint(const MouseEventArgs& e) {
	for(int i = 0; i < shapeVector.size(); i++) {
		shapeVector[i]->Draw(e.Graphics); // 多态调用,各负其责
	}
	// ...
	Form::OnPaint(e);
}

重新认识面向对象

  • 隔离变化
    从宏观层面来看,面向对象的构建的构建方式更能是适应软件的变化,能将变化所带来的影响减为最小。
  • 各司其职
    从微观层面来讲,面向对象的方式更强调各个类的“职责”,由于需求变化导致的新增类型不应该影响原来类型的实现——是所谓各负其责。
  • 对象是什么
    从语言层面来讲:对象封装了代码和数据。
    从规格层面来讲:对象是一系列可被使用的公共接口。
    从概念层面来讲:对象是拥有某种责任的抽象

设计原则

1、依赖倒置原则

  • 高层模块(稳定)不应该依赖底层模块(变化),二者都应该依赖抽象(稳定)
  • 抽象(稳定)不应该依赖实现细节(变化),实现细节应该依赖于抽象(稳定)
    总而言之,是易变化的依赖于稳定的,如果反过来,稳定的依赖变化的,稳定也就变得不稳定了。
    上述分解的做法中:
    高层模块(MainForm)依赖低层模块(Line,Rect),违背了依赖倒置原则。
    上述抽象做法中:
    高层模块(MainForm)和低层模块(Line,Rect)均依赖抽象(Shape),
    实现细节(Line,Rect)依赖于抽象(Shape)
    符合依赖倒置原则。

2、开闭原则

  • 对扩展开放,对更改关闭
  • 类模块应该是可扩展的,但是不可修改
    上述分解的做法中:
    MainForm中,如果增加一个圆,那么需要在MainForm中增加circleVector,这样的话就对MainForm需要进行修改,违背了开闭原则。

3、单一职责原则

  • 一个类应该仅有一个引起它变化的原因
  • 变化的方向隐含着类的责任

4、里氏替换原则

  • 子类只能扩展父类的功能,而不能改写父类的方法,子类必须保证替换父类的时候不能改变原有的逻辑。
  • 继承表达类型抽象

5、接口隔离原则

  • 不应该强迫客户程序依赖他们不用的方法。
  • 接口应该小而完备。该public,piublic,不需要的就protect,private

6、优先使用对象组合,而不是继承

  • 类继承通常为“白象复用”,对象组合通常为“黑箱复用”,
  • 继承在某种程度上破坏了封装性,子类父类耦合度高,
  • 而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低。

7、封装变化点

  • 使用封装来创建对象的分界层,让设计者可以在分界层的一侧进行修改,而不会对另一侧产生不良影响,从而下实现层次间的松耦合

8、针对接口编程,而不是针对实现编程

  • 不将变量类型声明为某个特定的具体类,而是声明为某个接口,
  • 客户程序无需知道对象的具体类型,只需要知道对象所具有的接口,
  • 减少系统中各部分的依赖,从而实现“高内聚,低耦合”的类型设计方案。
    上述分解做法中,MainForm依赖Line,Rect,就是针对实现了,违背了此原则。而抽象做法中,MainForm依赖Shape,就是针对接口了。
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-06 23:08:36  更:2022-04-06 23:08:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 0:02:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码