设计模式(2)--七大原则(java实现版)

前一篇博客已经对设计模式的概念和单一职责原则、接口隔离原则、依赖倒转原则进行了讲解,这一篇博客将会为大家讲解里氏替换原则、开闭原则,迪米特法则,合成复用原则。

里式替换原则

1.里氏替换原则在1988年,由麻省理工学院的一位姓里的女士提出的。

2.继承包含这样一层含义:父类中已经实现好的方法,实际上是在设定规范和契约,虽然不强制所有的子类必须遵守这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。

3.继承在给程序带来灵活性的同时也带来的弊端,比如使用继承会给程序带来入侵性,增加了对象间的耦合性。如果一个类被继承,那么当这个类需要修改的时候必须考虑到所有子类的兼容性。

4.里氏替换原则---在使用继承的时候,子类尽量不要重写父类的方法。

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

我们看一下下面的例子。

public class A {
	//返回两个数的差
	public int func1(int num1,int num2) {
		return num1 - num2;
	}
	
	public static void main(String[] args) {
		A a = new B();
		//这里原意是两个数相减。
		System.out.println(a.func1(12, 2));
	}
}

class B extends A{
	//这里重写了A类的方法可能是忘记了父类有这个方法
	public int func1(int num1, int num2) {
		return num1 + num2;
	}
	
	public int func2(int num1,int num2) {
		return func1(num1,num2) + 9;
	}
}

这样就有可能会造成程序的错误。为了解决这种错误,我们通用的做法是原来的父类的子类都基础一个更通俗的基类,原有的基础关系去掉,采用依赖,聚合,组合的方式。

UML类图如下

代码如下

public class Liskov {
	public static void main(String[] args) {
		B b = new B();
		System.out.println(b.func1(10, 2));
	}
}

abstract class Base{
	
}

class A extends Base{
	public int func1(int num1,int num2) {
		return num1 - num2;
	}
}

class B extends Base{
	//如果需要使用A类的方法,使用组合关系。
	private A a = new A();
	
	public int func1(int num1,int num2) {
		return a.func1(num1, num2);
	}
	
	public int func2(int num1,int num2) {
		return func1(num1,num2)+9;
	}
}

这样就不会无意中写错B类的func1方法了。

开闭原则

1.一个软件实体(比如:类),模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。

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

(需求变更的时候应该做到只更改提供方的代码,不用更改客户端的代码。可以参考上一篇博客中依赖倒转原则的实例代码。)

下面是没有遵循开闭原则的示例

public class Ocp1 {
	public static void main(String[] args) {
		GraphicEditor editor = new GraphicEditor();
		editor.drawShape(new Recrangle());
		editor.drawShape(new Circle());
		editor.drawShape(new Triangle());
	}
}

class GraphicEditor{
	public void drawShape(Shape e) {
		if(e.m_type == 1) {
			drawRectangle(e);
		}else if(e.m_type == 2){
			drawCircle(e);
		}else if(e.m_type == 3) {
			drawTriangle(e);
		}
	}
	
	public void drawRectangle(Shape r) {
		System.out.println("矩形");
	}
	
	public void drawCircle(Shape r) {
		System.out.println("圆形");
	}
	public void drawTriangle(Shape r) {
		System.out.println("三角形");
	}
}


class Shape{
	public int m_type;
}

class Recrangle extends Shape{
	public Recrangle() {
		super.m_type = 1;
	}
}

class Circle extends Shape{
	public Circle() {
		super.m_type = 2;
	}
}

class Triangle extends Shape{
	public Triangle() {
		super.m_type = 3;
	}
}

可以看到我们每增加一种shape类还需要更改GraphicEditor类,GraphicEditor类相对于shape类就是客户端,因为GraphicEditor类依赖了shape类。这就不符合开闭原则(对扩展开放,对客户端修改关闭)

下面是遵守开闭原则的示例代码

public class Ocp2 {
	public static void main(String[] args) {
		GraphicEditor editor = new GraphicEditor();
		editor.drawShape(new Recrangle());
		editor.drawShape(new Circle());
		editor.drawShape(new Triangle());
	}
}

class GraphicEditor{
	public void drawShape(Shape shape) {
		shape.draw();
	}
}


abstract class Shape{
	public abstract void draw();
}


class Recrangle extends Shape{

	public void draw() {
		System.out.println("绘制矩形");
	}
	
}

class Circle extends Shape{
	public void draw() {
		System.out.println("绘制圆形");
	}
}

class Triangle extends Shape{
	public void draw() {
		System.out.println("绘制三角形");
	}
}

这样不管扩展多少个Shape类都不用更改GraphicEditor类。符合了开闭原则

 

迪米特法则

1.一个对象应该对其他对象保持最少的了解

2.类与类关系越密切,耦合度越大。

3.迪米特法则又称最少知道原则,一个类对自己依赖的类知道的越少越好。

4.迪米特法则还要个更简单的定义:只和直接的朋友通信。(每个类都会和其他的类耦合,只要这两个类有耦合关系我们就称之为朋友,耦合的方式有很多种,依赖,关联,组合,聚合等,我们将出现在成员变量,返回值,参数的类称为直接朋友,出现在局部变量的不是直接的朋友,也就是说陌生的类不要出现在局部变量中。)

下面是违反迪米特法则的示例

import java.util.ArrayList;
import java.util.List;

public class demeter {
	public static void main(String[] args) {
		SchoolManager schoolManager = new SchoolManager();
		schoolManager.printAllEmployee(new CollegeManager());
	}
}

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

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}
	
}
//学院的员工类
class CollegeEmployee{
	private String id;

	public String getId() {
		return id;
	}

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


/**
 * 学院管理类
 *
 */
class CollegeManager{
	//返回所有学院员工
	public List<CollegeEmployee> getAllCollege(){
		List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
		for(int i = 0 ; i < 10 ; i++) {
			CollegeEmployee employee = new CollegeEmployee();
			employee.setId("学院员工"+i);
			list.add(employee);
		}
		return list;
	}
}
/**
 * 学校总部管理类
 *
 */
class SchoolManager{
	//返回所有学校总部的员工
	public List<Employee> getAllEmployee(){
		List<Employee> employees = new ArrayList<Employee>();
		for (int i = 0; i < 10; i++) {
			Employee employee = new Employee();
			employee.setId("学校总部员工"+i);
			employees.add(employee);
		}
		return employees;
	}
	
	/**
	 * 该方法完成输出学校总部和学院员工信息 
	 * 
	 * 在本类Employee、CollegeManager是直接朋友
	 * CollegeEmployee类不是直接朋友但是出现在了局部变量里面。违反了迪米特法则
	 */
	public void printAllEmployee(CollegeManager collegeManager) {
		//获得所有的学校总部员工
		List<Employee> employees = getAllEmployee();
		for (Employee employee : employees) {
			System.out.println(employee.getId());
		}
		
		//获得所有学院员工
		List<CollegeEmployee> employeess = collegeManager.getAllCollege();
		for (CollegeEmployee collegeEmployee : employeess) {
			System.out.println(collegeEmployee.getId());
		}
	}
}

可以看到CollegeEmployee类并不是SchoolManager类的直接朋友但是出现在了局部变量里面,违反了迪米特法则。

下面是遵守迪米特法则的示例

import java.util.ArrayList;
import java.util.List;

public class Demeter1 {
	public static void main(String[] args) {
		SchoolManager schoolManager = new SchoolManager();
		schoolManager.printAllEmployee(new CollegeManager());
	}
}

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

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}
	
}
//学院的员工类
class CollegeEmployee{
	private String id;

	public String getId() {
		return id;
	}

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


/**
 * 学院管理类
 *
 */
class CollegeManager{
	//返回所有学院员工
	public List<CollegeEmployee> getAllCollege(){
		List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
		for(int i = 0 ; i < 10 ; i++) {
			CollegeEmployee employee = new CollegeEmployee();
			employee.setId("学院员工"+i);
			list.add(employee);
		}
		return list;
	}
	//输出所有学院的员工
	public void printAllCollege() {
		List<CollegeEmployee> employees = getAllCollege();
		for (CollegeEmployee collegeEmployee : employees) {
			System.out.println(collegeEmployee.getId());
		}
	}
}
/**
 * 学校总部管理类
 *
 */
class SchoolManager{
	//返回所有学校总部的员工
	public List<Employee> getAllEmployee(){
		List<Employee> employees = new ArrayList<Employee>();
		for (int i = 0; i < 10; i++) {
			Employee employee = new Employee();
			employee.setId("学校总部员工"+i);
			employees.add(employee);
		}
		return employees;
	}
	
	/**
	 * 该方法完成输出学校总部和学院员工信息 
	 */
	public void printAllEmployee(CollegeManager collegeManager) {
		//获得所有的学校总部员工
		List<Employee> employees = getAllEmployee();
		for (Employee employee : employees) {
			System.out.println(employee.getId());
		}
		//输出所有学院员工信息
		collegeManager.printAllCollege();
	}
}

这样就保证了SchoolManager类都是和直接朋友建立联系了。

迪米特法则的注意事项和细节。

1.迪米特法则的核心是降低类与类直接的耦合

2.迪米特法则只是降低了类与类之间的耦合关系,并不是完全没有依赖。类与类之间不可能完全没有依赖。

合成复用原则

1.合成复用原则:尽量使用合成聚合的方式,而不是使用继承。继承会将子类和父类的耦合性增强。

2.一个类只是调用另一个类的方法应该使用依赖、组合、聚合的方式。

下面是没有遵守合成复用原则的示例

public class A {
	public void print() {
		System.out.println("A类的输出方法");
	}
	
	public void printA() {
		System.out.println("这是A类的输出方法");
	}
	public static void main(String[] args) {
		B b = new B();
		b.print();
	}
}


class B extends A{
	@Override
	public void print() {
		super.print();
		System.out.println("B类的输出方法");
	}
}

如果此时B类和A类没有逻辑上的继承关系,B类只是调用A类的方法那么,不应该使用继承,因为B类不想调用A类的printA方法,但是也被动的拥有了printA方法。

下面是遵守合成复用原则的示例

public class A {
	public void print() {
		System.out.println("A类的输出方法");
	}
	public void printA() {
		System.out.println("这是A类的输出方法");
	}
}

class B{
	private A a = new A();
	
	public void print() {
		a.print();
		System.out.println("B类的输出方法");
	}
}

这样B类通过组合的方式依赖A类,可以有选择性的调用A类的方法,也不会出现被动拥有A类方法的情况。

到这里设计模式的七大原则就讲完了,本人后续还会写设计模式系列的博客。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值