设计模式(学习笔记)

设计模式的概念

在此引用一个建筑学家,克里斯托佛·亚历山大(Christopher Alexander)的原话,“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该解决方案而不必做重复的劳动。”

软件设计的金科玉律是“复用”,而设计模式代表着代码的最佳实践,软件开发中它的使用便是为了重用代码等,以达到工程代码编制工程化。

通常所说的设计模式隐含的表示“面向对象设计模式”。

面向对象

面向对象的思维模型

面向对象分成两个模型:底层思维和抽象思维。

底层思维:向下,如何把握机器底层从微观理解对象构造(语言构造,编译转换,内存模型,运行时机制)。

抽象思维:向上,如何将我们的周围世界抽象为程序代码(面向对象,组件封装,设计模式,架构模式)。

抽象

由于对象的复杂性,我们无法掌握它的全部特征,选择忽视它的非本质细节,而去处理泛化和理想化了的对象模型,谓之抽象。

向下

深入理解面向对象的三大机制:

封装:隐藏内部实现

继承:复用现有代码

多态:改写对象行为

向上

 深刻把握面向对象机制所带来的抽象意义,理解如何使用这些机制来表达现实世界,掌握什么是“好的面向对象设计”。

理解面向对象

软件设计的固有的复杂性的根本原因在于变化(客户需求变化、市场环境变化等),变化是复用的天敌,而面向对象设计的最大优势便是“抵御变化”。

隔离变化

从宏观层面看来看,面向对象的构建方式更能适应软件的变化,能将变化所带来的影响降到最小。

各司其职

从微观层面来看,面向对象的方式更强调各个类的“责任”;由于需求变化导致的新增类型不应该影响原来类型的实现--即是各司其职。

何为对象

从语言实现层面上来看,对象封装了代码和数据;

从规格层面上来看,对象是一系列可被使用的公共接口;

从概念层面上来看,对象是某种拥有责任的抽象。

面向对象的八大设计原则

依赖倒置原则(DIP)

高层模块(稳定)不应该依赖于底层模块(变化),二者都应该依赖于抽象(稳定);

抽象(稳定)不应该依赖于实现细节(变化),实现细节应该依赖于抽象(稳定)。

//Shape.h
class Shape{
private:
	virtual void Draw(const Graphics& g)=0;
	virtual ~Shape() { }
};
//Point.h
class Point{
public:
	int x;
	int y;
};
//Line.h
class Line: public Shape{
private:
	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(Pens.Red, 
			start.x, start.y,end.x, end.y);
	}
};
//Rectangle.h
class Rect: public Shape{
private:
	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.DrawRectangle(Pens.Red,
			leftUp,width,height);
	}
};
//Mainform.cpp
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);
};

解析:

Mainform(高层模块)----<依赖于>------>Shape(抽象);

Line||Rect(低层模块)----<依赖于>------>Shape(抽象);//不可在Shape抽象类内部里处理Line||Rect,抽象不依赖实现细节

开放封闭原则(OCP)

对拓展开放,对更改封闭;

类模块应该是可拓展的,但是不可修改。

解析:如以上代码示例,添加一个画圆形的功能,我们无需更改Mainform类模块,只需添加多个文件即可。

//Circle.h
class Circle : public Shape{
private:
    //...
public:
	//实现自己的Draw,负责画自己
	virtual void Draw(const Graphics& g){
		g.DrawCircle(Pens.Red,
			...);
	}
};

单一职责原则(SRP)

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

Liskov替换原则(LSP)

子类必须能够替换它们的基类(IS-A);继承表达类型抽象。

接口隔离原则(ISP)

不应该强迫客户程序依赖它们不用的方法;接口应该小而完备。

组合复用原则(CRP)

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

封装变化点

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

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

不将变量类型声明为某个特定的具体类,而是声明为某个接口;客户程序无需获知对象具体的数据类型,只需要知道对象所具有的接口,减少系统中各部分的依赖关系,从而实现“高内聚,松耦合”的类型设计方案。

如若以上代码示例中,将

    vector<Shape*> shapeVector;

更改为

    vector<Shape> shapeVector;

vector添加对象,会出现内存拷贝,效率太慢,而指针只占用4个字节,效率会提升很多,同时将降低程序代码的耦合度。

设计模式分类

从目的来看

创建型模式(creational)模式

将对象的部分创建工作延迟到子类或者其他对象,从而应对需求变化为对象创建时具体类型实现引来的冲击。

结构型(structural)模式

通过类继承或者对象组合获得更灵活的结构,从而应对需求变化为对象的结构带来的冲击。

行为型(behavioral)模式

通过类继承或者对象组合来划分类和对象间的职责,从而应对需求变化为对个交互对象带来的冲击。

从范围来看

类模式处理类与子类的静态关系

对象模式处理对象间的动态模式

从封装变化角度对模式分类

组件协作

Template Method

Observer/Event

Strategy

对象性能

Singleton

Flyweight

数据结构

Composite

Iterator

Chain of Resposibility

单一职责

Decorator

Bridge

接口隔离

Facade

Proxy

Mediator

Adapter

行为变化

Command

Visitor

对象创建

Factory Method

Abstract Factory

Prototype

Builder

状态变化

Memento

State

领域问题Interpreter

重构与模式

面向对象的设计模式是“好的设计模式”,所谓“好的面向对象设计”指的是那些满足“应对变化,提高复用”的设计。

现代软件设计的特征是“需求的频繁变化”。设计模式的要点是“寻找变化点,然后在变化点处应用设计模式,从而来更好的应对需求变化”;“什么时候、什么地点应用设计模式”比“理解设计模式结构本身”更为重要。

设计模式的应用不宜先入为主,一上来就使用设计模式是对设计模式的最大误用。没有一步到位的设计模式;敏捷软件开发实践提倡的“重构获得模式”是目前公认的最好的使用设计模式的方法。

重构关键技法

静态----->动态

早绑定----->晚绑定

继承----->组合

编译时依赖----->运行时依赖

紧耦合---->松耦合

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值