java设计模式(一)——6大原则

设计模式的目的

编写软件的过程中,程序员面临开着耦合性,内聚性以及可维护性、可拓展性,重用性,灵活性等多方面的挑战,设计模式就是为了让程序(软件),具有更好的

  1. 代码重用性(即:相同功能的代码,不能重复编写);
  2. 可读性(即:编程规范性,便于其他程序员阅读与理解);
  3. 可拓展性(即:当需要增加新的功能时,非常的方便,称为可维护);
  4. 可靠性(即:当我们增加新的功能时,对原有的功能没有影响);
  5. 使程序呈现高内聚,低耦合的特性。

设计模式的六大原则

设计模式的原则,其实就是程序员在编程时,应当遵守的原则,也是各种设计模式的基础(即:设计模式为什么这样设计的依据)。

设计模式的6大原则,单一职责原则,开放封闭原则,里式替换原则,依赖导致原则,迪米特原则和接口隔离原则。


单一职责原则

就一个类而言,应该仅有一个引起它变化的原因、

通俗的讲就是我们不要让一个类承担过多的职责,如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。

这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到破坏。

比如我们会看到一些 Android 开发者在写 Activity 中 写Bean 文件,网络数据处理,如果有列表的话,Adapter也写在 Activity中。至于这么做的原因,除了简单粗暴,好找也没什么理由了,那么把其拆分到其他类岂不是更好找?如果Activity过于臃肿,行数过多,显然不是什么好事。

如果我们要修改Bean 文件,网络处理和 Adapter 都需要上这个Activity 来修改,就会导致引起该 Activity 变化的原因太多,我们在版本维护时也比较头痛。这也就严重违背了定义: 就一个类而言,应该仅有一个引起它变化的原因。

单一职责的划分界限不是很清晰,很多时候就要靠个人经验来界定,因此它是一个饱受争议却又极其重要的原则

代码演示

方案一:

public class SingleResponsibility1 {
	public static void main(String[] args) {
		Vehicle vehicle = new Vehicle(); 
		vehicle.run("摩托车"); 
		vehicle.run("汽车"); 
		vehicle.run("飞机");
	}

}
// 交通工具类
// 方式 1
// 1. 在方式 1  的 run 方法中,违反了单一职责原则
// 2. 解决的方案非常的简单,根据交通工具运行方法不同,分解成不同类即可
class Vehicle {
	public void run(String vehicle) {
		System.out.println(vehicle + "  在公路上运行....");
	}
}

方案二:

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

		RoadVehicle roadVehicle = new RoadVehicle(); 
		roadVehicle.run("摩托车"); 
		roadVehicle.run("汽车");
		AirVehicle airVehicle = new AirVehicle();
		airVehicle.run("飞机");
	}
}
//方案 2 的分析
//1. 遵守单一职责原则
//2. 但是这样做的改动很大,即将类分解,同时修改客户端
//3. 改进:直接修改 Vehicle 类,改动的代码会比较少=>方案 3
class RoadVehicle {
	public void run(String vehicle) {
		System.out.println(vehicle + "公路运行");
	}
}

class AirVehicle {
	public void run(String vehicle) {
		System.out.println(vehicle + "天空运行");
	}
}
class WaterVehicle {
	public void run(String vehicle) {
		System.out.println(vehicle + "水中运行");
	}
}

方案三:

public class SingleResponsibility3 {
	public static void main(String[] args) {
		Vehicle2 vehicle2	= new Vehicle2();
		vehicle2.run("汽车");
		vehicle2.runWater("轮船"); 
		vehicle2.runAir("飞机");
	}
}

//方式 3 的分析
//1. 这种修改方法没有对原来的类做大的修改,只是增加方法
//2. 这里虽然没有在类这个级别上遵守单一职责原则,但是在方法级别上,仍然是遵守单一职责
class Vehicle2 {
	public void run(String vehicle) {
		System.out.println(vehicle + "  在公路上运行....");
	}
	public void runAir(String vehicle) {
		System.out.println(vehicle + "  在天空上运行....");

	}
	public void runWater(String vehicle) {
		System.out.println(vehicle + "  在水中行....");
	}
}

单一职责原则注意事项和细节

  1. 降低类的复杂度,一个类只负责一项职责。

  2. 提高类的可读性,可维护性

  3.  降低变更引起的风险

  4.  通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则

接口隔离原则(Interface Segregation Principle)

建立单一接口,不要建立庞大臃肿的接口:尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图建立一个一个很庞大的接口供所有依赖他的类调用。采取接口隔离原则对接口进行约束时,要注意以下几点:

  • 接口尽量小,但是要有限度。对接口进行细化可以提高程序设计的灵活性;但是如果过小,则会造成接口数量过多,使设计复杂化。所以,一定要适度。
  • 为依赖接口的类定制服务,只暴露给调用的类他需要的方法,他不需要的方法则隐藏起来,只有专注的为一个模块提供定制服务,才能建立最小的依赖关系。
  • 为提高内聚,减少对外交互。接口方法尽量少用public 修饰。接口是对外的承诺,承诺越少对系统的开发越有利,变更的风险也越少。

代码演示 

方案一:没有使用接口隔离原则代码

public class Segregation1 {
	public static void main(String[] args) {
		
	}
}
interface Interface1 { 
	void operation1(); 
	void operation2();
	void operation3(); 
	void operation4(); 
	void operation5();
}
class B implements Interface1 {
	public void operation1() {
		System.out.println("B  实现了 operation1");
	}
	public void operation2() {
		System.out.println("B  实现了 operation2");
	}
	public void operation3() {
		System.out.println("B  实现了 operation3");
	}
	public void operation4() {
		System.out.println("B  实现了 operation4");
	}
	public void operation5() {
		System.out.println("B  实现了 operation5");
	}
}
class D implements Interface1 {
	public void operation1() {
		System.out.println("D  实现了 operation1");
	}
	public void operation2() {
		System.out.println("D  实现了 operation2");
	}
	public void operation3() {
		System.out.println("D  实现了 operation3");
	}
	public void operation4() {
		System.out.println("D  实现了 operation4");
	}
	public void operation5() {
		System.out.println("D  实现了 operation5");
	}
}
class A { //A 类通过接口 Interface1  依赖(使用) B 类,但是只会用到 1,2,3 方法
	public void depend1(Interface1 i) {
		i.operation1();
	}
	public void depend2(Interface1 i) {
		i.operation2();
	}
	public void depend3(Interface1 i) {
		i.operation3();
	}
}
class C { //C 类通过接口 Interface1  依赖(使用) D 类,但是只会用到 1,4,5 方法
	public void depend1(Interface1 i) {
		i.operation1();
	}
	public void depend4(Interface1 i) {
		i.operation4();
	}
	public void depend5(Interface1 i) {
		i.operation5();
	}

}

方案二:

 A 通过接口 Interface1 依赖类 B,类 C 通过接口 Interface1 依赖类 D,如果接口 Interface1 对于类 A 和类 C来说不是最小接口,那么类 B 和类 D 必须去实现他们不需要的方法

将接口 Interface1 拆分为独立的几个接口 A 和类 C 分别与他们需要的接口建立依赖关系也就是采用接口隔离原则

接口 Interface1 中出现的方法,根据实际情况拆分为三个接口

public class Segregation1 {
	public static void main(String[] args) {
		A a = new A();
		a.depend1(new B()); // A 类通过接口去依赖 B 类
		a.depend2(new B()); 
        a.depend3(new B());
		C c = new C();
		c.depend1(new D()); // C 类通过接口去依赖(使用)D 类
		c.depend4(new D()); 
        c.depend5(new D());
	}
}

// 接口 1
interface Interface1 {
	void operation1();
}
// 接口 2
interface Interface2 {
	void operation2();
	void operation3();
}
// 接口 3
interface Interface3 {
	void operation4();
	void operation5();
}

class B implements Interface1, Interface2 {
	public void operation1() {
		System.out.println("B  实现了 operation1");
	}
	public void operation2() {
		System.out.println("B  实现了 operation2");
	}
	public void operation3() {
		System.out.println("B  实现了 operation3");
	}
}



class D implements Interface1, Interface3 {
	public void operation1() {
		System.out.println("D  实现了 operation1");
	}
	public void operation4() {
		System.out.println("D  实现了 operation4");
	}
	public void operation5() {
		System.out.println("D  实现了 operation5");
	}
}
class A { // A 类通过接口 Interface1,Interface2  依赖(使用) B 类,但是只会用到 1,2,3 方法
	public void depend1(Interface1 i) {
		i.operation1();
	}
	public void depend2(Interface2 i) {
		i.operation2();
	}
	public void depend3(Interface2 i) {
		i.operation3();
	}
}
class C { // C  类通过接口 Interface1,Interface3  依赖(使用) D 类,但是只会用到 1,4,5 方法
	public void depend1(Interface1 i) {
		i.operation1();
	}
	public void depend4(Interface3 i) {
		i.operation4();
	}
	public void depend5(Interface3 i) {
		i.operation5();
	}
}

依赖倒转原则

高层模块(调用端)不应该依赖底层模块,两者都应该依赖于抽象。抽象不应该依赖于细节(实现类),细节应该依赖于抽象。

在Java中,抽象指接口或者抽象类,两者都是不能直接被实例化;细节就是实现类,实现接口或者继承抽象类而产生的就是细节,也就是可以加上一个关键字 new 产生的对象。高层模块就是调用端,低层模块就是具体实现类。 依赖倒置原则在 java 中的表现就是,模块间的依赖通过抽象发生,实现类之间不发生直接依赖关系,其依赖关系就是通过接口或者抽象类产生的。如果类与类直接依赖细节,那么就会直接耦合。如此一来,就会同时修改依赖者代码,这样限制了可扩展性。

抽象不应该依赖细节,细节应该依赖抽象,

依赖倒转(倒置)的中心思想是面向接口编程;

 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成

代码演示 :

方案一:

public class DependecyInversion {
	public static void main(String[] args) { 
		Person person = new Person(); 
		person.receive(new Email());
	}
}
class Email {
	public String getInfo() {
		return "电子邮件信息: hello,world";
	}
}
//完成 Person 接收消息的功能
//方式 1 分析
//1. 简单,比较容易想到
//2. 如果我们获取的对象是 微信,短信等等,则新增类,同时 Perons 也要增加相应的接收方法
//3. 解决思路:引入一个抽象的接口 IReceiver, 表示接收者,  这样 Person 类与接口 IReceiver 发生依赖
//	因为 Email, WeiXin  等等属于接收的范围,他们各自实现 IReceiver  接口就 ok,  这样我们就符号依赖倒转原则
class Person {
	public void receive(Email email ) {
		System.out.println(email.getInfo());
	}
}


方案二:依据依赖倒转原则

public class DependecyInversion {
	public static void main(String[] args) {
		//客户端无需改变
		Person person = new Person(); person.receive(new Email());
		person.receive(new WeiXin());
	}
}

//定义接口
interface IReceiver {
	public String getInfo();
}
class Email implements IReceiver {
	public String getInfo() {
		return "电子邮件信息: hello,world";
	}
}
//增加微信
class WeiXin implements IReceiver {
	public String getInfo() {
		return "微信信息: hello,ok";
	}
}
//方式 2
class Person {
	//这里我们是对接口的依赖
	public void receive(IReceiver receiver ) {
		System.out.println(receiver.getInfo());
	}
}

赖倒转原则的注意事项和细节:

  1. 低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好;
  2. 变量的声明类型尽量是抽象类或接口, 这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化;
  3. 继承时遵循里氏替换原则。

里氏替换原则

所有引用基类(父类)的地方必须能透明的使用其子类的对象

在软件中将一个基类对象替换成其子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用子类对象。里式替换原则是实现开放封闭原则的重要方式之一。由于使用基类对象的地方都可以使用子类对象, 因此在程序中尽量使用基类类型来对对象进行定义,而在运行时在确定其子类类型,用子类对象来替换父类对象。在使用里式替换原则是需要注意以下几个问题:

子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法。根据里式替换原则,为了保证系统的扩展性,在程序中通常使用父类来定义。如果一个方法只存于子类中,在父类中不提供相应的声明,则无法在以父类定义的对象中使用该方法。

我们运用里式替换原则时,尽量把父类设计为抽象类或接口,让子类继承父类或实现父接口,并实现在父类中声明的方法、运行时,子类实例替换父类实例,我们可以很方便的扩展系统功能,同时无序修改原有子类的代码;增加新的功能可以通过增加一个新的子类来实现。里式替换原则是开放封闭原则的具体实现手段之一。

在java语言中,在编译阶段,java编译器会检查一个程序是否符合里式替换原则。这是一个与实现无关,纯语法意义上的检查,但Java编译器的检查是有局限性的。

如果对每个类型为 T1 的对象 o1,都有类型为 T2 的对象 o2,使得以 T1 定义的所有程序 P 在所有的对象 o1 代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象。

在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法;

里氏替换原则告诉我们继承实际上让两个类耦合性增强了在适当的情况下可以通过聚合组合,依赖 解决问题

 代码演示:

方案一

public class Liskov {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		A a = new A();
		System.out.println("11-3=" + a.func1(11, 3)); System.out.println("1-8=" + a.func1(1, 8));
		System.out.println("-----------------------");
		B b = new B();
		System.out.println("11-3=" + b.func1(11, 3));
		//这里本意是求出 11-3 System.out.println("1-8=" + b.func1(1, 8));
		// 1-8 System.out.println("11+3+9=" + b.func2(11, 3));
	}
}
// A 类
class A {
	// 返回两个数的差
	public int func1(int num1, int num2) {
		return num1 - num2;
	}
}

// B 类继承了 A
// 增加了一个新功能:完成两个数相加,然后和 9 求和
class B extends A {
	//这里,重写了 A 类的方法, 可能是无意识
	public int func1(int a, int b) {
		return a + b;
	}
	public int func2(int a, int b) {
		return func1(a, b) + 9;
	}
}

方案二:

我们发现原来运行正常的相减功能发生了错误。原因就是类 B 无意中重写了父类的方法,造成原有功能出现错误。在实际编程中我们常常会通过重写父类的方法完成新的功能这样写起来虽然简单,但整个继承体系的,复用性会比较差。特别是运行多态比较频繁的时候;

通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖,聚合,组合关系代替。

public class Liskov {
	public static void main(String[] args) {
		A a = new A();
		System.out.println("11-3=" + a.func1(11, 3)); 
		System.out.println("1-8=" + a.func1(1, 8));
		System.out.println("-----------------------");
		B b = new B();
		//因为 B 类不再继承 A 类,因此调用者,不会再 func1 是求减法//调用完成的功能就会很明确
		System.out.println("11+3=" + b.func1(11, 3));//这里本意是求出 11+3
		System.out.println("1+8=" + b.func1(1, 8));// 1+8 System.out.println("11+3+9=" + b.func2(11, 3));
		//使用组合仍然可以使用到 A 类相关方法System.out.println("11-3=" + b.func3(11, 3));//  这里本意是求出 11-3
	}
}
//创建一个更加基础的基类
class Base {
	//把更加基础的方法和成员写到 Base 类
}

// A 类
class A extends Base {
	// 返回两个数的差
	public int func1(int num1, int num2) {
		return num1 - num2;
	}
}
// B 类继承了 A
// 增加了一个新功能:完成两个数相加,然后和 9 求和
class B extends Base {
	//如果 B 需要使用 A 类的方法,使用组合关系
	private A a = new A();
	//这里,重写了 A 类的方法, 可能是无意识
	public int func1(int a, int b) {
		return a + b;
	}
	public int func2(int a, int b) {
		return func1(a, b) + 9;
	}
	//我们仍然想使用 A 的方法
	public int func3(int a, int b) {
		return this.a.func1(a, b);
	}
}

 开闭原则

    类,模块,函数等应该是可以扩展的,但是不可以修改

开放封闭有两个含义:一个是对于扩展是开放,另一个是对于修改是封闭的。

对于开发者莱索,需求肯定是变化的,但是有新需求,我们就要把类重新改一遍,这显然是令人头痛的,所以我们设计程序时,面对需求的改变要尽可能得保证相对稳定,尽量通过扩展的方式来实现变化,而不是通过修改原有的代码来实现。

假设我们要实现一个列表,一开始只有查询的功能,后来产品又要新增 添加 功能,过几天又要增加 删除 功能。大多数人的做法是写一个方法,然后通过传入不同的值控制方法实现不同的功能。但是如果又要新增功能,我们还得修改方法。用开发封闭原则解决就是增加一个抽象的功能类,让添加,删除和查询作为这个抽象功能类的子类。这样如果我们再新增功能,你就会发现自己无须修改原有的类,只需要添加一个功能类的子类实现功能类的方法就可以了

当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。

 

代码演示:

方案一 :

public class Ocp {
	public static void main(String[] args) {
		//使用看看存在的问题
		GraphicEditor graphicEditor = new GraphicEditor();
		graphicEditor.drawShape(new Rectangle());
		graphicEditor.drawShape(new Circle()); 
		graphicEditor.drawShape(new Triangle());
	}
}
//这是一个用于绘图的类 [使用方]
class GraphicEditor {
	//接收 Shape 对象,然后根据 type,来绘制不同的图形
	public void drawShape(Shape s) {
		if (s.m_type == 1)
			drawRectangle(s);
		else if (s.m_type == 2)
			drawCircle(s);
		else if (s.m_type == 3)
			drawTriangle(s);
	}
	//绘制矩形
	public void drawRectangle(Shape r) {
		System.out.println("  绘制矩形 ");
	}
	//绘制圆形
	public void drawCircle(Shape r) {
		System.out.println("  绘制圆形 ");
	}
	//绘制三角形
	public void drawTriangle(Shape r) {
		System.out.println("  绘制三角形 ");
	}
}
//Shape 类,基类
class Shape {
	int m_type;
}
class Rectangle extends Shape {
	Rectangle() {
		super.m_type = 1;
	}
}
class Circle extends Shape {
	Circle() {
		super.m_type = 2;
	}
}

//新增画三角形
class Triangle extends Shape {
	Triangle() {
		super.m_type = 3;
	}
}

 方案一分析:

  1. 优点是比较好理解,简单易操作。

  2. 缺点是违反了设计模式的 ocp 原则,即对扩展开放(提供方),对修改关闭(使用方)。即当我们给类增加新功能的时候,尽量不修改代码,或者尽可能少修改代码。

  3. 比如我们这时要新增加一个图形种类 三角形,我们需要做如下修改,修改的地方较多。

方案二:

思路:把创建 Shape 类做成抽象类并提供一个抽象的 draw 方法,子类去实现即可这样我们有新的图形种类时,只需要让新的图形类继承 Shape并实现 draw 方法即可使用方的代码就不需要修 -> 满足了开闭原则。

public class Ocp {
	public static void main(String[] args) {
		GraphicEditor graphicEditor = new GraphicEditor(); 
		graphicEditor.drawShape(new Rectangle()); 
		graphicEditor.drawShape(new Circle()); 
		graphicEditor.drawShape(new Triangle()); 
		graphicEditor.drawShape(new OtherGraphic());
	}

}
//这是一个用于绘图的类 [使用方]
class GraphicEditor {
	//接收 Shape 对象,调用 draw 方法
	public void drawShape(Shape s) {
		s.draw();
	}
}

//Shape 类,基类
abstract class Shape {
	int m_type;
	public abstract void draw();//抽象方法
}

class Rectangle extends Shape {
	Rectangle() {
		super.m_type = 1;
	}
	@Override
	public void draw() {
		// TODO Auto-generated method stub
		System.out.println("  绘制矩形 ");
	}
}
class Circle extends Shape {
	Circle() {
		super.m_type = 2;
	}
	@Override
	public void draw() {
		// TODO Auto-generated method stub
		System.out.println("  绘制圆形 ");
	}
}
//新增画三角形
class Triangle extends Shape {
	Triangle() {
		super.m_type = 3;
	}
	@Override
	public void draw() {
		// TODO Auto-generated method stub
		System.out.println("  绘制三角形 ");
	}
}
//新增一个图形
class OtherGraphic extends Shape {
	OtherGraphic() {
		super.m_type = 4;
	}
	@Override
	public void draw() {
		// TODO Auto-generated method stub
		System.out.println("  绘制其它图形 ");
	}
}

迪米特法则

 一个软件实体应当少的与其他实体发生相互作用。

这也被称最好知识原则。如果一个系统符合迪米特原则,那么当其中某一个模块发生修改时,就会尽量少的影响其他模块。迪米特原则要求我们在设计系统是,应该尽量减少对象之间的交互。如果两个对象之间不必彼此直接通向,那么这两个对象就不应当发生任何直接的相互作用。如果其中的一个对象需要调用另一个对象的某一个方法,则可以通过第三者转发这个调用。简言之,就是通过引入一个合理的第三者来降低现有对象之间的耦合度。在将迪米特原则运用到系统设计中时,要注意以下几点:

  • 在类的划分上,应当尽量创建松耦合的类。类之间的耦合越低,就越有利于复用。一个处在松耦合中的类一旦被修改,则不会对关联的类造成太大波及。
  • 在类的结构上,每一个类都应当尽量降低其成员变量和成员函数的访问权限。
  • 在对其他类的引用上,一个对象对其他对象的引用应当降到最低。

迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部对外除了提供的 public 方法不对外泄露任何信息;

迪米特法则还有个更简单的定义:只与直接的朋友通信;

直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联组合,聚合等。其中,我们称出现成员变量方法参数方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友也就是说,生的类最好不要以局部变量的形式出现在类的内部。

代码演示:

有一个学校,下属有各个学院和总部,现要求打印出学校总部员工 ID 和学院员工的 id

方案一:

public class Demeter1 {
	public static void main(String[] args) {
		//创建了一个 SchoolManager  对象
		SchoolManager schoolManager = new SchoolManager(); //输出学院的员工 id 和 学校总部的员工信息
		schoolManager.printAllEmployee(new CollegeManager());
	}
}

//学校总部员工类
class Employee {
	private String id;

	public void setId(String id) {
		this.id = id;
	}

	public String getId() {
		return id;
	}
}
//学院的员工类
class CollegeEmployee {
	private String id;

	public void setId(String id) {
		this.id = id;
	}
	public String getId() {
		return id;
	}
}
//管理学院员工的管理类
class CollegeManager {
	//返回学院的所有员工
	public List getAllEmployee() {
		List list = new ArrayList(); 
		for (int i = 0; i < 10; i++) { //这里我们增加了 10 个员工到 list
			CollegeEmployee emp = new CollegeEmployee();
			emp.setId("学院员工 id= " + i);
			list.add(emp);
		}
		return list;
	}
}
//学校管理类
//分析 SchoolManager  类的直接朋友类有哪些 Employee、CollegeManager 
//CollegeEmployee 不是 直接朋友 而是一个陌生类,这样违背了 迪米特法则
class SchoolManager {
	//返回学校总部的员工
	public List getAllEmployee() {
		List list = new ArrayList();
		for (int i = 0; i < 5; i++) { //这里我们增加了 5 个员工到 list
			Employee emp = new Employee(); 
			emp.setId("学校总部员工 id= " + i); 
			list.add(emp);
		}
		return list;
	}
	//该方法完成输出学校总部和学院员工信息(id)
	void printAllEmployee(CollegeManager sub) {
		//分析问题
		//1. 这里的 CollegeEmployee  不是  SchoolManager 的直接朋友
		//2. CollegeEmployee  是以局部变量方式出现在 SchoolManager
		//3. 违反了 迪米特法则
		//获取到学院员工
		List list1 = sub.getAllEmployee(); 
		System.out.println("------------学院员工------------"); 
		for (CollegeEmployee e : list1) {
			System.out.println(e.getId());
		}
		//获取到学校总部员工
		List list2 = this.getAllEmployee(); 
		System.out.println("------------学校总部员工------------"); 
		for (Employee e : list2) {
			System.out.println(e.getId());
		}
	}
}

方案二:方案一设计的问题在于 SchoolManager 中,CollegeEmployee 类并不是 SchoolManager 类的直接朋友 (分析);按照迪米特法则,应该避免类中出现这样非直接朋友关系的耦合,对代码按照迪米特法则 进行改进;

//客户端

public class Demeter1 {
	public static void main(String[] args) { 
		System.out.println("~~~使用迪米特法则的改进~~~"); 
		//创建了一个 SchoolManager  对象
		SchoolManager schoolManager = new SchoolManager(); //输出学院的员工 id 和 学校总部的员工信息
		schoolManager.printAllEmployee(new CollegeManager());
	}

}
//学校总部员工类
class Employee {

	private String id;

	public void setId(String id) {
		this.id = id;
	}
	public String getId() {
		return id;
	}
}
//学院的员工类
class CollegeEmployee {
	private String id;
	public void setId(String id) {
		this.id = id;
	}
	public String getId() {
		return id;
	}
}
//管理学院员工的管理类
class CollegeManager {
	//返回学院的所有员工
	public List getAllEmployee() {
		List list = new ArrayList();

		for (int i = 0; i < 10; i++) { //这里我们增加了 10 个员工到 list 
			CollegeEmployee emp = new CollegeEmployee(); 
			emp.setId("学院员工 id= " + i);
			list.add(emp);
		}
		return list;
	}
	//输出学院员工的信息
	public void printEmployee() {
		//获取到学院员工
		List list1 = getAllEmployee(); 
		System.out.println("------------学院员工------------"); 
		for (CollegeEmployee e : list1) {
			System.out.println(e.getId());
		}
	}

}

//学校管理类
//分析 SchoolManager  类的直接朋友类有哪些 Employee、CollegeManager 
//CollegeEmployee 不是 直接朋友 而是一个陌生类,这样违背了 迪米特法则
class SchoolManager {
	//返回学校总部的员工
	public List getAllEmployee() {
		List list = new ArrayList();
		for (int i = 0; i < 5; i++) { //这里我们增加了 5 个员工到 list
			Employee emp = new Employee(); 
			emp.setId("学校总部员工 id= " + i);
			list.add(emp);
		}
		return list;
	}
	//该方法完成输出学校总部和学院员工信息(id)
	void printAllEmployee(CollegeManager sub) {
		sub.printEmployee();

		//获取到学校总部员工
		List list2 = this.getAllEmployee();
		System.out.println("------------学校总部员工------------");
		for (Employee e : list2) {
			System.out.println(e.getId());
		}
	}
}

总结: 

设计原则核心思想

  1. 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
  2. 针对接口编程,而不是针对实现编程。
  3. 为了交互对象之间的松耦合设计而努力。

 

 

 

你可能感兴趣的:(java,设计模式)