• 设计模式2--设计原则


    设计模式--设计原则

    重新认识面向对象

    • 理解隔离变化

      • 从宏观层面来看,面向对象的构建方式更能适应软件的变化,能将变化所带来的影响减为最小
    • 各司其职

      • 从微观层面来看,面向对象的方式更强调各个类的“责任”

      • 由于需求变化导致的新增类型不应该影响原来类型的实现—— 是所谓各负其责

    • 对象是什么?

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

      • 从规格层面讲,对象是一系列可被使用的公共接口。

      • 从概念层面讲,对象是某种拥有责任的抽象。

    面向对象的8大设计原则

    1:依赖倒置原则(DIP)

    • 高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定) 。
    • 抽象(稳定)不应该依赖于实现细节(变化) ,实现细节应该依赖于抽象(稳定)。

    2:开放封闭原则(OCP)

    • 对扩展开放,对更改封闭。
    • 类模块应该是可扩展的,但是不可修改。

    3:单一职责原则(SRP)

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

    4:Liskov 替换原则(LSP)

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

    5:接口隔离原则(ISP)

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

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

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

    7:封装变化点

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

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

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

    GOF-23 模式分类

    从目的来看:

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

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

    • 行为型(Behavioral)模式:通过类继承或者对象组合来划分类与对象间的职责,从而应对需求变化为多个交互的对象带来的冲击。

    从范围来看:

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

    • 对象模式处理对象间的动态关系。

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

    组件协作:

    • Template Method

    • Observer / Event

    • Strategy

    单一职责:

    • Decorator

    • Bridge

    对象创建:

    • Factory Method

    • Abstract Factory

    • Prototype

    • Builder

    对象性能:

    • Singleton

    • Flyweight

    接口隔离:

    • Façade

    • Proxy

    • Mediator

    • Adapter

    状态变化:

    • Memento

    • State

     数据结构:

    • Composite

    • Iterator

    • Chain of Resposibility

     行为变化:

    • Command

    • Visitor

     领域问题:

    • Interpreter

    重构获得模式 Refactoring to Patterns

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

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

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

    重构关键技法

    静态 -->动态

    早绑定 --> 晚绑定

    继承 --> 组合

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

    紧耦合 --> 松耦合

    示例代码

    不好的代码设计

    MainForm1.cpp

    class MainForm : public Form {
    private:
    	Point p1;
    	Point p2;
    
    	vector<Line> lineVector;
    	vector<Rect> rectVector;
    	//改变
    	vector<Circle> circleVector;
    
    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);
    	}
    	//改变
    	else if (...){
    		//...
    		circleVector.push_back(circle);
    	}
    
    	//...
    	this->Refresh();
    
    	Form::OnMouseUp(e);
    }
    
    void MainForm::OnPaint(const PaintEventArgs& 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.DrawRectangle(Pens.Red,
    			rectVector[i].leftUp,
    			rectVector[i].width,
    			rectVector[i].height);
    	}
    
    	//改变
    	//针对圆形
    	for (int i = 0; i < circleVector.size(); i++){
    		e.Graphics.DrawCircle(Pens.Red,
    			circleVector[i]);
    	}
    
    	//...
    	Form::OnPaint(e);
    }
    

    Shape1.h

    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;
        }
    
    };
    
    //增加
    class Circle{
    
    };
    
    该设计的缺陷
    • 当加入需要画园的需求的时候,需要大面积更改源码,主要违背封闭原则。

    优化代码结构

    MainForm2.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);
    };
    
    
    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));
    	}
    	//改变
    	else if (...){
    		//...
    		shapeVector.push_back(circle);
    	}
    
    	//...
    	this->Refresh();
    
    	Form::OnMouseUp(e);
    }
    
    void MainForm::OnPaint(const PaintEventArgs& e){
    
    	//针对所有形状
    	for (int i = 0; i < shapeVector.size(); i++){
    
    		shapeVector[i]->Draw(e.Graphics); //多态调用,各负其责
    	}
    
    	//...
    	Form::OnPaint(e);
    }
    

    Shape2.h

    class Shape{
    public:
    	virtual void Draw(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(Pens.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.DrawRectangle(Pens.Red,
    			leftUp,width,height);
    	}
    
    };
    
    class Circle : public Shape{
    public:
    	//实现自己的Draw,负责画自己
    	virtual void Draw(const Graphics& g){
    		g.DrawCircle(Pens.Red,
    			...);
    	}
    
    };
    
    优势分析
    • 通过添加一个基类Shape并在中添加画这个动作的纯虚函数virtual void Draw(const Graphics& g)=0;,之后所有的形状都继承该基类Shape各自实现自己对应的画的动作,在MainForm::OnPaint(const PaintEventArgs& e)函数中进行循环多态调用shapeVector[i]->Draw(e.Graphics);
    • 该设计的基类Shape是个稳定的抽象的接口,之后所有的子类都依赖该基类,满足依赖倒置原则,封闭原则,等。
  • 相关阅读:
    面试官:能解释一下javascript中bind、apply和call这三个函数的用法吗
    接口测试基础004----常见的post请求中的参数形式
    JMeter基础017----JMeter自带录制功能使用
    JMeter基础016----JMeter文件上传与下载
    JMeter基础018----生成HTML测试报告
    JMeter基础019----JMeter接口功能、性能、自动化测试
    接口测试基础002----HTTP请求、RESTful风格
    Jmeter报错001--- ERROR o.a.j.JMeter: Uncaught exception in thread Thread[AWT-EventQueue-0,6,main] java.lang.InternalError: Could not bind shell folder to interface
    Jmeter基础014----组件总结、FTP(了解)及其他不重要组件
    Jmeter基础013----图形监视器之监控服务器资源
  • 原文地址:https://www.cnblogs.com/DXGG-Bond/p/13441951.html
Copyright © 2020-2023  润新知