设计模式简介及面向对象设计原则

news2024/11/17 23:28:28

文章目录

  • 前言
  • 一、什么是设计模式
    • 1、从面向对象谈起
    • 2、深入理解面向对象
    • 3、软件设计固有的复杂性
    • 4、软件设计复杂的根本原因——“变化”
    • 5、如何解决复杂性?
    • 6、软件设计的目标
  • 二、常用设计模式及分类
    • 1、常用的七种设计模式
    • 2、设计模式分类
  • 三、面向对象设计原则
    • 1、面向对象设计,为什么?
    • 2、重新认识面向对象
    • 3、面向对象设计原则
    • 4、面向接口设计
  • 四、C++ 源码分析
    • 1、分解思维伪代码
    • 2、抽象思维伪代码
    • 3、对比两种思维


前言

  • 不希望学习设计模式的小伙伴最终掌握的是一堆的代码和技巧,其实那些不重要,重要的是建立了一套思维方式和模型。
  • 23 种设计模式中有些模式今天已经不流行了,有些模型已经被语言机制替代了,有些模式你可能常常会忘记,但这些都不重要,重要的是设计原则,因为有了这些设计原则,你有可能发明自己的模式,你也可以理解未来千千万万其他领域的模式。
  • 有很多朋友有一种错误观点就是把学习模式当做算法来学习,这是不对的,所有的设计模式都是依赖下面讲的面向对象设计原则推导出来的,如果它违背了设计原则,那么这个模式就是错误的。

一、什么是设计模式

“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动”。

总之,设计模式就是为了解决某类重复出现的问题而出现的一套成功或有效的解决方案

1、从面向对象谈起

在这里插入图片描述
①、底层思维:向下,如何把握机器底层从微观理解对象构造

  • 语言构造
  • 编译转换
  • 内存模型
  • 运行时机制

②、抽象思维:向上,如何将我们的周围世界抽象为程序代码

  • 面向对象
  • 组件封装
  • 设计模式
  • 架构模式

2、深入理解面向对象

①、向下:深入理解三大面向对象机制

  • 封装,隐藏内部实现
  • 继承,复用现有代码
  • 多态,改写对象行为

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

3、软件设计固有的复杂性

建筑商从来不会去想给一栋已建好的100层高的楼房底下再新修一个小地下室——这样做花费极大而且注定要失败。然而令人惊奇的是,软件系统的用户在要求作出类似改变时却不会仔细考虑,而且他们认为这只是需要简单编程的事。

4、软件设计复杂的根本原因——“变化”

  • 客户需求的变化
  • 技术平台的变化
  • 开发团队的变化
  • 市场环境的变化

5、如何解决复杂性?

  • 分解
    • 人们面对复杂性有一个常见的做法:即分而治之,将大问题分解为多个小问题,将复杂问题分解为多个简单问题。
  • 抽象
    • 更高层次来讲,人们处理复杂性有一个通用的技术,即抽象。由于不能掌握全部的复杂对象,我们选择忽视它的非本质细节,而去处理泛化和理想化了的对象模型。

6、软件设计的目标

什么是好的软件设计?软件设计的金科玉律:复用

二、常用设计模式及分类

1、常用的七种设计模式

单例模式、工厂方法模式、抽象工厂模式、代理模式、装饰器模式、观察者模式和责任链模式。

2、设计模式分类

设计模式根据工作的目的,分为创建型模式、结构型模式和行为型模式三类。

创建型模式:单例模式、工厂方法模式、抽象工厂模式、创建者模式、原型模式。

结构型模式:适配器模式、代理模式、装饰器模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

三、面向对象设计原则

1、面向对象设计,为什么?

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

2、重新认识面向对象

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

3、面向对象设计原则

  • ①、依赖倒置原则(DIP)
    • 高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定)
    • 抽象(稳定)不应该依赖于实现细节(变化) ,实现细节应该依赖于抽象(稳定)
  • ②、开放封闭原则(OCP)
    • 对扩展开放,对更改封闭
    • 类模块应该是可扩展的,但是不可修改
  • ③、单一职责原则(SRP)
    • 一个类应该仅有一个引起它变化的原因
    • 变化的方向隐含着类的责任
  • ④、Liskov 替换原则(LSP)
    • 子类必须能够替换它们的基类(IS-A)
    • 继承表达类型抽象
  • ⑤、接口隔离原则(ISP)
    • 不应该强迫客户程序依赖它们不用的方法
    • 接口应该小而完备
  • ⑥、优先使用对象组合,而不是类继承
    • 类继承通常为“白箱复用”,对象组合通常为“黑箱复用”
    • 继承在某种程度上破坏了封装性,子类父类耦合度高
    • 而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低
  • ⑦、封装变化点
    • 使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧进行修改,而不会对另一侧产生不良的影响,从而实现层次间的松耦合
  • ⑧、针对接口编程,而不是针对实现编程
    • 不将变量类型声明为某个特定的具体类,而是声明为某个接口
    • 客户程序无需获知对象的具体类型,只需要知道对象所具有的接口
    • 减少系统中各部分的依赖关系,从而实现“高内聚、松耦合”的类型设计方案

4、面向接口设计

产业强盛的标志:接口标准化

四、C++ 源码分析

注:以下所列代码均为伪代码,没有按照 C++ 标准编码,很多具体细节都省略了,例如字段作为实现细节应声名为 private,我们这里声明为了 public,通过 new 申请的内存没有 delete 释放,只为讲解区别

下面是在 ui 界面画点、线及矩形的伪代码,基于当前的代码,又新增了画圆的功能。

1、分解思维伪代码

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{
	...
};

2、抽象思维伪代码

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,
			...);
	}
};

3、对比两种思维

①、分解思维
在这里插入图片描述

②、抽象思维
在这里插入图片描述
比较上面两种思维可以得出,针对新增画圆的功能来说,分解思维需要修改很多处,抽象思维需要修改的代码更少,抽象思维相比于分解思维更简单且代码可维护性更强,代码更具有健壮性。


我的qq:2442391036,欢迎交流!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/451827.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

半导体封装用除泡烤箱真空压力可编程PID控制的解决方案

摘要&#xff1a;真空压力除泡机和除泡烤箱在电子行业的应用十分广泛&#xff0c;但现有除泡机存在的最大问题是选择了开关式阀门&#xff0c;无法实现真空和压力既准确又快速的控制。为此&#xff0c;本文提出了升级改造技术方案&#xff0c;即采用双向PID控制器和快速电动球阀…

Docker部署开源密码管理器Bitwarden, 并申请免费ssl证书自动刷新永不过期

GitHub传送阵 废话 出于一种习惯&#xff0c;我基本上不会在不同的应用上使用相同的密码&#xff0c;这种习惯使得我需要在备忘录上不胜其烦地记录大量的账号密码&#xff0c;每次登录一个系统&#xff0c;如果chrome的密码管理器不可用&#xff0c;我就需要打开备忘录检索。…

基于matlab使用波束成形生成 802.11ad 波形

一、前言 本示例说明如何使用WLAN工具箱和相控阵系统工具箱对带有相控阵的IEEE 802.11ad DMG波形进行波束成形。 二、介绍 IEEE 802.11ad 定义了工作在 60 GHz 的定向千兆位 &#xff08;DMG&#xff09; 传输格式。为了克服在 60 GHz 下遇到的大路径损耗&#xff0c;IEEE 802.…

进阶必看 | 有关BIMer强推的5本书,看过的都竖大拇指!

大家好&#xff0c;还是我&#xff0c;建模助手。 本期的主题都是围绕着&#xff1a;热点。除了建模助手的品牌资讯之外&#xff0c;还有一些与行业相关的热点。 这不&#xff0c;4月23日是正好的世界读书日&#xff0c;给大家搞一波书籍推荐&#xff01; 小编认为&#xff…

【Dubbo核心 详解二】Dubbo服务消费的详解

✅创作者:陈书予 🎉个人主页:陈书予的个人主页 🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区 🌟专栏地址: Dubbo专栏 文章目录 引言介绍 Dubbo 服务消费的详解的目的和背景概述 Dubbo 服务消费的过程和核心概念一、Dubbo 服务消费的基础知识1. Dubbo 服务消费的架…

动力节点springsecurity笔记-SpringSecurity 集成thymeleaf

15 SpringSecurity 集成thymeleaf 此项目是在springsecurity-12-database-authorization-method 的基础上进行 复制springsecurity-12-database-authorization-method 并重命名为springsecurity-13-thymeleaf 15.1 添加thymeleaf依赖 | org.springframework.boot spring-…

h5逻辑_解决h5页面嵌入ios兼容性问题

安全区域 如下图所示&#xff5e; 蓝色部分为安全区域。处于安全区域内的内容不受圆角、齐刘海、小黑条的影响。 若是将h5页面嵌入app中&#xff0c;就需要进行适配—> 让h5页面展示在安全区域内。 tips: 安全区域是在ios11之后并且是iPhoneX及以上机型才有的。 因此我们只…

【微信小程序】详解behaviors,如何使用behaviors

一&#xff0c;behaviors 1.1什么是 behaviors&#xff1f; behaviors 是小程序中&#xff0c; 用于实现组件间代码共享的特性 &#xff0c;类似于 Vue.js 中的 “mixins”。 1.2behaviors 的工作方式 每个 behavior 可以包含一组 属性、数据、生命周期函数和方法 。组件引…

【PE】inline hook的实现

【PE】inline hook的实现 hook思路 最基本的5字节的hook思路如下&#xff0c;有了这个思路&#xff0c;可以用更多的方式进行hook 通过修改目标函数开头的5个字节为jmp …&#xff0c;劫持程序执行流跳转过去之后&#xff0c;再把API开头5字节改回来&#xff08;UnHook&…

Servlet 入门到精通(六)

上一篇博客的传送门&#xff1a;Servlet 入门到精通&#xff08;五&#xff09; Servlet Filter 又称 Servlet 过滤器&#xff0c;它是在 Servlet 2.3 规范中定义的&#xff0c;是 Servlet 中的一个组件&#xff0c;是设计模式中责任链模式的一种经典实现。能够对 Servlet 容器…

SpringBoot启动自动执行sql脚本

在开发当中我们每次发布服务都需要手动执行脚本&#xff0c;然后重启服务&#xff0c;而SpringBoot有服务启动自动执行sql脚本的功能的&#xff0c;可以为我们省去手动执行脚本的这一步&#xff0c;只需要部署新的服务即可。 这个功能是SpringBoot自带的不需要引入额外的依赖&a…

Spring Boot 提取内存密码

访问 /actuator/heapdump 下载内存&#xff0c;提取密码 select * from java.util.LinkedHashMap$Entry x WHERE (toString(x.key).contains("password"))

学习系统编程No.21【进程间通信之共享内存】

引言&#xff1a; 北京时间&#xff1a;2023/4/16/21:53&#xff0c;刚刚把新文章发出去&#xff0c;开完班会回来&#xff0c;本来上篇博客在昨天就能发的&#xff0c;昨天下午打了一下午的羽毛球之后&#xff0c;饭都没吃&#xff0c;躺在床上&#xff0c;准备睡觉&#xff…

Spring五大类注解 || Bean的更简单存储

目录 前言&#xff1a; 五大类注解 Controller Service Repository Component Configuration JavaEE标准分层 阿里分层结构 BeanName命名规则 方法注解 Bean 注入方式取Bean 属性注入 Setter注入 构造方法注入 Resource 前言&#xff1a; 使用Spring容器&…

Linux-驱动开发-基础温习

一、裸机开发和驱动开发的区别&#xff1a; 裸机开发&#xff1a;底层&#xff08;相对于linux来说&#xff09;&#xff0c;库 二、linux驱动开发-根据各种框架进行开发 1、 外设比较多&#xff0c;资源多&#xff0c;资料非常少&#xff0c;官方的SDK;直接操作寄存器不显示…

Python 基础(十):元组

❤️ 博客主页&#xff1a;水滴技术 &#x1f338; 订阅专栏&#xff1a;Python 入门核心技术 &#x1f680; 支持水滴&#xff1a;点赞&#x1f44d; 收藏⭐ 留言&#x1f4ac; 文章目录 一、声明元组二、访问元组三、修改元组变量四、遍历元组五、切片六、常用函数和方法6.…

SpringBoot实现导出Excel功能

1 问题背景 需求要做一个导出excel的功能 2 前言 本篇着重阐述后端怎么实现&#xff0c;前端实现的部分只会粗略阐述。该实现方案是经过生产环境考验的&#xff0c;不是那些拿来练手的小demo。本文阐述的方案可以借鉴用来做毕设或者加到自己玩的项目中去。 3 实现思路 后端查询…

103. 二叉树的锯齿形层序遍历【191】

难度等级&#xff1a;中等 上一篇算法&#xff1a; 104. 二叉树的最大深度【75】 力扣此题地址&#xff1a; 103. 二叉树的锯齿形层序遍历 - 力扣&#xff08;Leetcode&#xff09; 1.题目&#xff1a;103. 二叉树的锯齿形层序遍历 给你二叉树的根节点 root &#xff0c;返回其…

p65 内网安全-域环境工作组局域网探针方案

数据来源 基本概念 DMZ区域&#xff1a;称为“隔离区”&#xff0c;也称‘’非军事化区/停火区” 工作组&#xff08;Work Group&#xff09;是局域网中的一个概念。它是最常见最简单最普通的资源管理模式&#xff0c;就是将不同的电脑按功能分别列入不同的组中&#xff0c;以…

完美解决丨except NameError:

示例如下&#xff1a; try: print(xx) except: print(xx is not defined) print(continue) 解决办法 第一种解决办法&#xff1a; try: print(xx) except NameError: print(xx is not defined) print(continue) 第二种解决办法&#xff1a; print(xx) if xx in locals() e…