Hao_YiYi

导航

作业二——读书笔记:软件设计原则&设计模式

所属课程 <班级链接>
作业要求 <作业要求链接>
作业目标 1. 培养自学习惯
2. 提升总结概括能力
3. 理解并掌握软件设计原则和设计模式
4. 改进代码编写习惯以及增强代码编写能力

一、读书总结

0、参考资料

名称 出处 作者
《软件设计的7大原则》 <链接> ——
《软件秘笈:设计模式那点事》 <链接> 郑阿奇

1、设计原则

1)开闭原则:

对外使用开放,对内修改关闭的原则。不改动原有类的方式来实现新的需求,利用抽象出来的接口或者具体类继承抽象类的方式来实现。能够在不改动原有代码的前提下实现新功能,增强程序的可扩展性,同时降低程序的维护成本。

2)单一职责原则:

一个类或函数只允许由单一的职责。如果一个类具备多种职责,那么导致类变化的原因就有多种,维护就变得困难。职责划分清晰,不但可以提高代码可读性,并且能够降低出错风险,降低维护成本。

3)依赖倒置原则:

针对接口编程,尽量不产生具体类的派生,而是以继承抽象类或实现接口来实现。将业务层归于上层模块,逻辑层、数据层归于底层模块。通过抽象来搭建框架,建立类与类的关联,以减少类间的耦合性,使得功能实现稳定,扩展性高,便于维护。

4)接口隔离原则:

客户端不依赖不需实现的接口。接口应该尽可能细化,使得其中的方法尽可能少;同时也不应过少,使得设计复杂化。能够避免同一接口包含不同职责,符合高内聚低耦合的思想。

5)迪米特法则:

一个类应该之和它的成员变量、方法输入、返回参数的类交流,而不应引入其他类,发生间接交流。能够有效降低类与类间的耦合,减少类与类间的关联度,使得协作更加直接明了。

6)里氏代换原则:

所有引用基类的地方需要透明地使用其子类的对象,也就是说子类对象可以替换其父类对象,而程序执行效果不变。在继承体系中,子类中可以增加自己特有的方法,也可以实现父类的抽象方法,但是不能重写父类的非抽象方法,以保证正确的继承关系。能够检验继承使用的正确性,约束继承在使用上的泛滥。

7)合成/聚合复用原则:

尽量使用合成/聚合,而不使用继承。其中合成表示整体与部分的生命周期相等,关系不能共享,代表部分的对象在每一时刻只能与一个对象发生合成关系。聚合则是部分可以是整体的一部分,也可以脱离整体。在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新对象通过向这些对象的委派达到复用已有功能的目的。复用所需依赖少,黑箱复用,每个新对象都可将焦点集中与一个任务上。
遵守设计原则不但能使我们的代码维护难度大幅降低,同时还能提高代码健壮性、可复用性,降低维护成本等。

2、设计模式

设计模式是软件设计中常见的典型解决方案。它们就够根据需求进行调整的预制蓝图,可用于解决代码中反复出现的问题。

常见的设计模式方案

  • 创造型模式:
    工厂方法、抽象工厂、生成器、原型、单例等

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

  • 行为模式:
    责任链、命令、迭代器、中介者、备忘录、观察者、状态、策略、模版方法、访问者等

由于设计模式的实际使用不是按部就班,一成不变的。这是需要根据实际情况去设计、去改变原有的设计模式,使之更适合当前的软件开发情况。

*下面以工厂方法模式为例进行分析:

  • 工厂方法模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。

  • 工厂方法经常用在以下两种情况中:

1、对于某个产品,调用者清楚地知道应该使用哪个具体工厂服务,实例化该具体工厂,生产出具体的产品来。
2、只需要一种产品,而不想知道也不需要知道究竟是哪个工厂为生产的,即最终选用哪个具体工厂的决定权在生产者一方,它们根据当前系统的情况来实例化一个具体的工厂返回给使用者,而这个决策过程这对于使用者来说是透明的。

二、读书心得

通过本次对设计原则及设计模式的学习,我深有感触,对于好的代码的编写更加充满希望并期盼自己能够对其有更加深入的学习与了解,使得自身代码编写能力有所提升,使程序注入灵魂。
在大一大二的时候,并不知道设计原则和设计模式是什么,只知道仅仅把从编程设计课本上学到的编程知识灵活运用到需要实现的功能对应的代码编写出来就万事大吉了。在多次完成实验和课设的实践过程中,都发现基本的功能只要是多加思索就能够将其解决,但是过程十分的痛苦。编程经验较少,水平较低,写出来的程序不够简洁,并且注释不够清晰明了。尤其是操作系统的那次实验课设,让我印象极其深刻。那次课设题目是多道批处理系统两级调度。虽然最后完成度达到了预期水平,但是过程是极其艰辛的。前后总共花费了五天的时间,推翻了三个版本(最后拿去答辩的是第四个版本)。当时用的是C语言,编写出来的函数十分的长,并且几乎每个函数都不符合单一职责原则。就拿下述函数为例进行分析:

int  jinm(JOB j[],int t){//外存排序以及进入内存,同时得知有无进入内存的作业 
	int b=0;
	int num=99;
	for(int i=0;i<JMAX;i++){
		outq[i].jnum=99;
	}
	for(int k=0;k<JMAX;k++){
		if(j[k].jarise){
			b++;
			j[k].arrive=1; 
		} 
		{
			outq[b].jnum=k;
			outq[b].prior=j[k].allpry;
			b++;
		}
	}
	if(b>0){
		int l[b];
		for(int x=0;x<b;x++){
			l[x]=outq[x].prior;
		}
		if(b>1)asort(l,b);
		for(int y=0;y<b;y++){
			for(int x=0;x<b;x++){
				if(outq[x].prior==l[y]){
					l[y]=outq[x].jnum;
				}
			}
		}
	}
	if(b>0){
		if(b>1)asortq(outq,b);
//		对于现有队列选择能进入内存的作业
		int c=0; 
		for(int z=0;z<b;z++){
			c= mscp(arr,j[l[z]].size);
//			printf("%d:%d\n",l[z]+1,j[l[z]].allpry);
			if(c>=0&&Mnum>=j[l[z]].disc){//进行资源分配
				num=l[z];
				Mnum-=j[l[z]].disc;
				mmark=c;
				jmalloc(l[z],j[l[z]].size,arr[3*c+2],arr);
				j[l[z]].jarise=0;
				printf("#作业 %d 被放入内存,当前内存分配情况如下:\n",l[z]+1);
				mshow(arr);
				printf("&当前磁带机数:%d\n",Mnum);
			} 
		}
		for(int i=0;i<JMAX;i++){
			for(int k=0;k<JMAX;k++){
				if(j[k].jarise&&j[k].allpry==JMAX-i){
					int c=mscp(arr,j[k].size);
					if(c>=0&&Mnum>=j[k].disc){//进行资源分配 
						num=k;
						Mnum-=j[k].disc;
						jmalloc(k,j[k].size,arr[3*c+2],arr);
						j[k].jarise=0;
						printf("#作业 %d 被放入内存,当前内存分配情况如下:\n",k+1);
						mshow(arr);
						printf("&当前磁带机数:%d\n",Mnum);
					} 
				}
			}
		}
	}
	return num;
}

这个函数是用于实现控制以及监控内存作业的。其中正如之前提到的单一职责原则,这里实现的功能有两个,所以是违反了这一原则,职责划分不明确,出错率大大提高,更会使函数难以利用。
代码冗长,注释少且不够清晰明了,可读性不高,且不易修改(前三个版本存在同样的问题,甚至更不堪入目)。并且,面向过程的程序编写是更为困难的。

就是在如此情况下编写出第一个字符数量较多但是杂乱无章、难以读写的程序。
这样的程序不仅在编写时十分的吃力,在后期对程序进行维护时所需成本更是超乎想象,甚至面临推翻重做的风险。

编写程序光是能够实现功能还是远远不够的,我们更需要写好的程序。编程即艺术,我们需要掌握一定的算法知识,懂得灵活利用设计模式,遵守设计原则,才能使我们的程序“活过来”,能够被高效地利用,也能够被有效地维护。为这一方面提升付出比学习一门编程语言更多的时间是值得的,编程语言是工具,而设计原则和设计模式则是赋予我们程序灵魂的存在。也希望在今后的学习过程中能够一直遵守设计原则,灵活运用设计模式,对设计原则和设计模式的理解及利用上有所突破以及创新。

三、相关截图

posted on 2022-03-01 14:20  Hao_YiYi  阅读(141)  评论(0)    收藏  举报