摘录设计模式之禅(第二版)

432 阅读6分钟

这是个积少成多的事情,会不间断的更新中...

设计模式是什么?它是一套理论,由软件界的先辈们总结出的一套可以反复使用的经验,它可以提高代码的可重用性,增强系统的可维护性,以及解决一系列的复杂问题。

*开闭原则(OCP):一个软件实体如类、模块和函数应该对扩展开发,对修改关闭。 *

迪米特法则也称为最少知识原则,虽然名字不同,但描述的的是同一个规则:一个对象应该对其他对象有最少的了解。通俗的讲,一个类应该对自己需要耦合或者调用的类知道的最少,你(被耦合或调用的类)的内部是如何复杂和我没关系,那是你的事情,我就知道你提供的这么多public方法,我就调用这么多,其他的我一概不关心。

第七大原则即合成复用原则。

代理模式

代理模式是一个使用率非常高的模式。P116

定义:为其他对象提供一种代理以控制对这个对象的访问。

中介者模式

定义:用一个中介对象封装一系列对象的交互,中介使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变他们之间的交互。P156

观察者模式

P419.png

P419.png

P423.png 场景类Client,它代表高层模块,或者说是非“近亲”模块的调用者。--P446

  • 接口隔离原则:建立单一接口,不要建立臃肿庞大的接口。

聚合

ProjectIterator实现Iterator接口,并且聚合了Project对象,也就是把Project对象作为本对象的成员使用。

装饰模式

装饰模式是一种比较常见的模式。

定义:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式比生成子类更为灵活。

  • 就增加功能来说,装饰模式相比生成子类更为灵活。--P189

  • 在实际的应用中类可以有多重职责,但是接口一定要职责单一。--P226

原型模式

  • 不通过new关键字来产生一个对象,而是通过对象复制来实现的模式就叫做原型模式。--P228

适配器模式

  • 适配器模式可以通过把非本系统接口的对象包装成本系统可以接受的对象,也是一种补偿模式。--P229

  • 如果超过两层继承,你就应该想想是不是设计出问题了,是不是应该找一条康庄大道,这是经验,并不是什么绝对的。

迭代器模式

它提供一种方法访问一个容器对象中各个元素,而又不需要暴露该对象内部的细节。

Java已经把迭代器给我们准备好了,我们再去写迭代器就有点多余了。所以这个迭代器模式也有点落寞了,基本上很少有项目再独立写迭代器了,直接使用Collection下的实现类就可以完美地解决问题。

桥梁模式

桥梁模式也叫做桥接模式,是一个比较简单的模式。

定义:将抽象和实现解耦,使得两者可以独立地变化。

门面模式

  • 对于子系统而言,门面仅仅是另外一个客户端而已。--P434

备忘录模式

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

  • 复制一个当前状态,保留下来,每次尝试失败后,都必须恢复到这个状态。成功以后即可走成功路线。--P443

在某一时间点的所有位置信息、心理信息、环境信息都属于状态。--P444

结果正确并不代表程序是最优的。--P446

一个类的职责应该是单一,保存状态及恢复状态由另外一个类来完成,那么我们把这个类取名为备忘录,这和大家经常在桌面上贴的那个便签是一个概念。--P446

访问者模式

访问者模式是一个相对简单的设计模式。

定义:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。--P316

在具体的实现类中调用访问者的方法。--P477

访问者模式的通用类图




//Employee--抽象元素
//CommonEmployee--具体元素
public class CommonEmployee extends Employee {
	

	//IVisitor--抽象访问者
	//visitor--具体访问者对象
	//决定哪一类访问者可以访问具体元素
	@Overrride
	public void accept(IVisitor visitor) {
		//访问者定义了可以访问哪些具体元素
		visitor.visitor(this);
	}

}

双分派

代码如下:





public interface Role {
	//演员要扮演的角色

	public void accept(AbsActor actor);
}

public class KungFuRole implement Role {
	//武功天下第一的角色

	@Override
	public void acccept(AbsActor actor) {
		actor.act(this);
	}


}

public class IdiotRole implement Role {
	//一个弱智的角色
	@Override
	public void acccept(AbsActor actor) {
		actor.act(this);
	}
}

//使用重载定义方法act()
public abstract AbsActor {

	public void act(Role role) {

		System.out.printIn("演员可以扮演任何角色");

	}

	public void act(KungFuRole role) {
		System.out.printIn("演员可以扮演功夫角色");
	}
	

}


//复写父类的方法act(KungFuRole role)
public class YoungActor extends AbsActor {

	@Override
	public void act(KungFuRole role) {
		System.out.printIn("喜欢饰演功夫角色");

	}
	
}


//复写父类的方法act(KungFuRole role)
public class OldActor extends AbsActor {
	
	@Override
	public void act(KungFuRole role) {
		System.out.printIn("年龄大了,不能饰演功夫角色了");
	}
}



public class Client {
	
	public static void main(String[] args) {

		Role role = new KungFuRole();

		AbsActor actor = new OldActor();

		actor.act(role);

		actor.act(new KungFuRole());

	}

}

  • 内存中有这么多对象并不表示这些对象正在被使用,估计很大一部分还没有被回收,垃圾回收器什么时候回收内存中的对象这是不确定的。应该适应共享技术--对象池减少对象数量。--P544

  • 抽象类增加构造函数,是为了提醒子类,你必须做这项工作,指定实现者特别是已经明确了实现者,则尽量清晰明确地定义出来。--P576

  • 在进行系统设计时,发现类的继承有N层时,可以考虑使用桥梁模式。--P579

解释器设计模式类图

image.png

直接朋友

耦合的方式有很多种,依赖、关联、聚合、组合等,其中,我们称出现成员变量,方法参数,方法返回值中的类为直接对象,而出现在局部变量中的类非直接对象,也就是说陌生的类最好不要以局部变量的形式出现在类的内部。


public class Student {
    //生命对象属性mSchool,是直接朋友
    private School mSchool;
    
    //方法参数schoolClass,是直接朋友
    public void setClass(SchoolClass schoolClass) {
        
    }
    //方法返回值对象locakTask,是直接朋友
    public SchoolTask getTask() {
        SchoolTask locakTask = new SchoolTask();
        return localTask;
    }
    
}