HIT 软构 第五章总结

前言

第四章讲了怎么可以提高代码的可复用度,第五章就是在提高可复用度之后再提高我们代码的可维护性和可扩展性,这一章的主要内容是面向可维护的设计模式,提高可维护性和可扩展性,才能延长一个软件的生命周期。
我们在编程的时候,可以经常问自己一下几个问题:

  • 设计结构是否足够简单?
  • 模块之间是否松散耦合?
  • 模块内部是否高度聚合?
  • 是否使用了非常深的继承树?
  • 是否使用了委派替换继承?
  • 代码的圈复杂度是否太高?
  • 是否存在重复代码?

模块化编程

我们在编写类或者函数的时候都尽可能的追求高内聚和低耦合。尽可能的使得函数功能单一,每个类的功能也单一,这就是模块化的思想。模块化编程十分有利于我们进行维护,因为我可以更快的定位bug,也更有利于我们进行扩展。高内聚的用白话讲就是功能尽可能单一统一,低耦合就是ADT或者模块方法之间的联系尽可能少,主要体现的地方就是传参少,一个方法如果有过多的参数那么一旦需要维护的时候限制就很多,因为参数确实很大程度上限制了方法适用的范围。

SOLID

SOLID是五种面向对象的设计原则:

  • SRP(单一责任原则),ADT责任单一
  • OCP(开放-封闭原则),面临变化的时候,尽可能避免改动源代码,而是用扩展的方式替代。
  • LSP(Liskov替换原则)子类可以完全替换父类
  • ISP(接口聚合原则)给客户端提供给的接口都应是必要的,没有多余的接口
    • DIP(依赖转置原则)抽象的模块不应依赖于具体的模块,而是应该反过来让具体的依赖于抽象,因抽象而诞生。

面向可维护性的设计模式

我们遵循上述的设计原则,给出了几种面向可维护性的设计模式,这些模式在很大程度上 能规范我们的设计思想,避免一些bug,使后续的维护和扩展变得更加容易。

工厂方法模式

我们一直强调减少客户端对我们内部代码实现的直接访问或联系,客户端在创建一个实例的时候,我们一般都是给出接口,并没有告知客户端具体的实现类,就是希望客户端不要依赖我内部的具体实现,也尽可能的避免客户端了解我内部的实现情况,所以我们在创建实例返回给客户端的时候通过接口中定义的静态的方法给客户端返回一个实例。这种思路叫 静态工厂方法,我们也可以使用工厂类的形式给用户返回具体实例,总之目的都是为了不让客户端直接接触具体的实现类 。

抽象工厂方法

相比于普通的工厂方法,抽象工厂模式提供接口以创建一组相关/相互依赖的对象 ,但不需要指明其具体类。如果说普通的工厂方法是要返回一个具体的实例,那么抽象工厂方法就是返回一组具体的实例。但这一组具体的实例是有特点的,就好比烤冷面一样的,第一个用户要放辣椒的加了火腿肠的烤冷面,第二个用户要不放辣椒加了鸡蛋的烤冷面,烤冷面就是一组实例,里面是固定的实例搭配组合。下图为一个代码实例。
在这里插入图片描述
其实本质上抽象工厂方法就是将多类实例的工厂方法组合在一起。

代理模式

代理模式有点像一个防火墙或中间商,某个对象比较敏感或者私密,不希望被客户端直接访问,我们就设置一个代理,通过代理来实现调用,这种代理也可能降低我们访问复杂对象的难度,就像代购一样的,要是自己飞去国外买那代价太高了,不如找代购。
这个模式和适配器模式很像,不过二者的用途不一样,适配器是用来提高兼容性的;代理模式则是隔离对复杂对象的访问,降低难度或者代价。

Observer模式

Observer模式从字面上理解就是观察,时时地观察,多对一的观察。一个很好的比喻就是“粉丝”对“偶像”的观察,粉丝们都希望能时刻了解偶像的一举一动,这是种一对多的观察。
怎么实现的呢?
可以看成一种业务,粉丝在偶像那里注册身份,偶像就可以对已注册的所有粉丝进行广播,有点像微博里的关注一样,你到你喜欢的偶像那里关注他,那他就能向你发布他的最新动态。

具体代码实现如下:
我们设计一个idou类,里面有粉丝列表,我们一旦更改了idou的状态,就要通知粉丝

public class Idou{
	private List<Observer> observers = new ArrayList<Observer>();//粉丝列表
	private String state;//idou的状态
	public String getState(){ return state;}
	//设置状态,并向粉丝们发布
	public void setState(String state){
		this.state = state;
		notifyAllObservers();
	}
	//粉丝关注
	public void attach(Observer fans){ observers.add(fans);}
	//遍历所有的粉丝,向粉丝发布动态
	private void notifyAllObserves(){
		for(Observer observer : observers)
			observer.update();
	}
}

创建一个抽象类或者接口作为观察者,里面有相应的方法,可以看到里面永久的保存了idou,这是一种永久的委派关系(当我觉得不一定要永久的,要是突然换了idou呢是吧hhhhh----来自多人运动时代)

//创建一个抽象类,作为观察者
public absract class Obserer{ 
 potected Idou idou;
 public abstract void update();
}

//创建一个Fans的具体实现类
public class Fans extends Observer{
	//创建一个具体的粉丝,在idou那里注册关注
	public Fans(Idou idou){
		this.idou = idou;
		this.idou.attach(this);
	}
	//重写update方法
	@Override
	public voi update(){
		System.out.println(idou.getState());
	}
}

上述实现就是一个Observer模式的具体实现例子,可以看到idou中调用了observer的方法,fans中也调用了idou的方法,这是一种双向的委派关系,以前我们经常用的都是单向的委派关系,observer模式的特点就是双向委派。

Visitor模式

根据OCP原则,我们在对方法进行扩展的时候,尽可能的不动原来的代码,visitor模式就是一个很好的扩展的设计模式。visitor模式的思想就是在方法中留下一个槽,留给以后扩展用的。打个比方,我们的电脑上都有很多的usb接口,这些usb接口就是留给以后扩展功能用的,比如接鼠标,键盘,耳机,摄像头,音响等等。visitor模式就很这个类似。
我们来看下面具体的代码实现:
还是idou那个例子,我在idou中增加了一个新的方法Unknownfunction(Visitor visitor),这个方法不知道是干什么的,但是先留下来了。然后我如果定义一个Visitor接口,里面也只有一个方法(如下面代码),假如idou要开演出会了,我实现visitor接口,在里面设置idou的状态。

//一个visitor接口,里面只有一个visitor方法
public interface Visitor{
	public int visit(Idou idou);
}
//具体实现类,表示idou正在进行演唱会
public class concertVisitor implements Visitor{
	@Override
	public int visit(Idou idou){
		idou.setState("正在x市举办演唱会");
		System.out.prinln(idou.getState());
	}
}

public class Idou{
	private List<Observer> observers = new ArrayList<Observer>();
	private String state;
	public String getState(){ return state;}
	public void setState(String state){
 		 this.state = state;
 		 notifyAllObservers();
 	}
 	public void attach(Observer fans){ observers.add(fans);}
 	private void notifyAllObserves(){
  		for(Observer observer : observers)
   			observer.update();
 		}
	}
	//新添加的方法,预留给visitor接口
	public int Unknownfunction(Visitor visitor){
		return visitor.visit(this);
	}
}

这就满足OCP原则的扩展,我并没有更改原来的代码,只是通过实现visitor接口来实现不同的功能,这是一种十分高效的扩展模式,而且还可以迭千层饼一样的不停地层层的扩展下去,有了一个接口,我还可以实现不同的实现类,用哪个的时候传哪个。需要注意的是,idou和visitor也是有相互调用的,也就是说他们之间也是一种委派的关系,所以委派的应用场景真的很大,可以和继承组合出各种设计模式。

状态模式

很多ADT都是有状态变量的,我们在设计状态的时候可能都是用if else或者switch进行状态的转换,这些转换操作都是在ADT内部实现的,但我们可以将这些状态委派出去,比如我上述的那个idou类中也有状态,当然我的状态很简单啊就是一个字符串,那万一我的状态要特别复杂有很多其他的操作或功能呢?
我们就可以将状态看作一个对象,编写它的ADT,比如实现一个State接口,然后各种各样的状态都是继承这个接口,在State接口中定义一个转换状态的方法,然后在具体的子类中重写转换方法,可以根据不同的状态制定不同的转换规则,自由度很大,还可以进行一些别的扩展之类的。比如像下
面这种:
在这里插入图片描述在这里插入图片描述

记忆模式

我更喜欢叫回溯模式,这个就更好理解了,就是实现一个ctl-z或ctrl-y的功能,我们保留某个对象的状态,保留之后可以对历史状态进行回溯,对象就能回到原来的状态,要是(人生也有这种功能就好了)。实现也很简单,就是定义一个用来保存历史记录的类,专门存档,每次想要回档就在这个类中找,我觉得大家应该能想象出来。

正则表达式

前面都是各种各样的设计模式,还有一个十分好用的东西就是正则表达式,它可以极大的简化我们对字符串的匹配筛选,比如我们想知道一个字符串是否包含某个字符’a’或’b’或’c’,string的方法,但不够灵活,你也可以一个个遍历,就更麻烦了,用正则表达式就很方便string.match(“a | b |c”);还有更麻烦的应用,比如在实验三里面判断读入的文件是否满足某种格式,那么长一段字符串,不可能一个个判断的,用正在表达式就是一句话的事。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值