前一篇博客已经对设计模式的概念和单一职责原则、接口隔离原则、依赖倒转原则进行了讲解,这一篇博客将会为大家讲解里氏替换原则、开闭原则,迪米特法则,合成复用原则。
里式替换原则
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类方法的情况。
到这里设计模式的七大原则就讲完了,本人后续还会写设计模式系列的博客。