理解架构中的设计原则

阅读: 评论:0

理解架构中的设计原则

理解架构中的设计原则

在使用面向对象的思想进行系统设计时,应遵循面向对象的设计原则,前人总结的7条分别是:单一职责原则、开闭原则、里氏替换原则、依赖注入原则、接口分离原则、迪米特原则和优先使用组合而不是继承原则。

单一职责原则(SRP-Single Responsibility Principle)

单一职责也就是开发人员经常说的“高内聚低耦合”,也就是说系统中的每一个对象都应该有一个单独的职责,对外只能提供一种功能,引起对象变化的原因也只有一个,所有的设计模式都遵循这一原则。通常一个类的职责越多,导致其变化的因素也就越多。一般情况我们设计类时会把该类有关的操作都组合在一起,这样的结果就是有可能将多个职责聚合到了一起,当这个类的某个职责发生变化时,很难避免其他部分不受影响,最终导致程序的脆弱和僵硬。解决办法就是分耦,将不同职责分别进行封装。

例如用户的属性和用户的行为被放在一个接口中申明,造成业务对象和业务逻辑混在一起,使接口有两种职责。

/*** JavaProject* Created by xian.juanjuan on 2017-7-10 10:59.*/
public interface Ijuanjuan {//身高double getShengao();void setShengao(double height);//体重double getTizhong();void setTizhong(double weight);//吃饭boolean chiFan(boolean hungry);//上班boolean shangBan(boolean flag);
}

分别定义属性和行为接口并分别实现

/*** BO:Bussiness Object*/
public interface IjuanjuanBO{//身高double getShengao();void setShengao(double height);//体重double getTizhong();void setTizhong(double weight);
}
public class JuanjuanBO implements IjuanjuanBO{private double height;private double weight;@Overridepublic double getShengao() {return height;}@Overridepublic double getTizhong() {return weight;}@Overridepublic void setShengao(double height) {this.height = height;}@Overridepublic void setTizhong(double weight) {this.weight = weight;}
}
/*** BL:Business Logic*/
public interface IjuanjuanBL{//吃完上班boolean chiFan(boolean hungry);boolean shangBan(boolean flag);
}
public class JuanjuanBL implements IjuanjuanBL{@Overridepublic boolean chiFan(boolean hungry) {if(hungry){System.out.println("吃大餐。。。");return true;}return false;}@Overridepublic boolean shangBan(boolean flag) {if (flag){System.out.println("上班中。。。");return true;}return false;}
}

这样需要修改用户属性的时候只需要对IjuanjuanBO这个接口进行修改,影响的也只是JuanjuanBO这个类,其余的类不受影响。

SRP原则的好处是消除耦合,减小因需求变化而引起代码僵化的局面。

里氏替换原则(LSP-Liskov Substitution Principle)

里氏替换原则的核心思想是:在任何父类出现的地方都可以用他的子类来代替。即同一个继承体系中的对象应该拥有共同的行为特征。也就是说只要父类出现的地方子类就能出现而,且替换为子类不会出现任何错误和异常,但是反过来,子类出现的地方替换为父类很可能就出问题了。

这一原则为良好的继承指定了一个规范:

  1. 子类必须完全实现父类的方法
  2. 子类可以有自己的特性
  3. 覆盖或者实现父类的方法时输入参数可以被放大
  4. 覆盖或者实现父类的方法时输出结果可以被缩小

规范3示例

package com.xianjj.principle;import java.util.Collection;
import java.util.HashMap;
import java.util.Map;/*** JavaProject* Created by xian.juanjuan on 2017-7-10 14:01.*/
public class Father {public Collection say(HashMap hashMap){System.out.println("father 被执行");return hashMap.values();}
}class Son extends Father{public Collection say(Map map) {System.out.println("son 被执行");return map.values();}
}class Home{public static void main(String[] args){invoke();}public static void invoke(){Son son = new Son();HashMap hashMap = new HashMap();son.say(hashMap);Map map = new HashMap<>();son.say(map);Father father = new Father();HashMap hashMap1 = new HashMap();father.say(hashMap1);}
}

执行结果

father 被执行
son 被执行
father 被执行

依赖注入原则(DIP-Dependence Inversion Principle)

依赖注入原则的核心思想:要依赖于抽象(抽象类或接口),不要依赖于具体的实现。在应用程序中,所有的类如果使用或依赖于其他类,都应该依赖于这些类的抽象类,而不是具体实现类。要求:开发人员应该针对接口编程

依赖注入原则三点说明:

  • 高层模块不应该依赖底层模块,两者都应该依赖于抽象
  • 抽象不应该依赖于细节
  • 细节应该依赖抽象

依赖注入的本质是通过抽象(抽象类或接口是不能实例化)使各个类或者模块之间实现彼此独立,不相互影响,实现模块间的送耦合。

依赖注入的三种实现方式:

  • 通过构造函数传递依赖对象(在构造函数中需要传递的参数是抽象类或接口)
  • 通过setter方法传递依赖对象(我们设置setXX方法中的参数是抽象类或接口)
  • 接口声明实现依赖对象

例如,涂涂只会煮面条

public class Tutu {//涂涂只会煮面条public void cook(Noodles noodles){noodles.eat();}
}class Noodles{public void eat(){System.out.println("涂涂吃面条");}
}class Eat{public static void main(String[] args){Tutu tutu = new Tutu();Noodles noodles = new Noodles();k(noodles);}
}

涂涂天天吃面条吃腻了,想吃水煮鱼,大闸蟹怎么办呢,于是涂涂开始学习做其他吃的,需要实现Ifood接口。

public class Tutu {public void cook(Ifood ifood){ifood.eat();}
}interface Ifood{public void eat();
}class Noodles implements Ifood{@Overridepublic void eat(){System.out.println("涂涂吃面条");}
}class Rice implements Ifood{@Overridepublic void eat() {System.out.println("涂涂吃米饭");}
}class Eat{public static void main(String[] args){Tutu tutu = new Tutu();//涂涂会煮面条Ifood noodles = new Noodles();k(noodles);//涂涂学会煮米饭Ifood rice = new Rice();k(rice);}
}

煮米饭和煮面条作为两个独立的模块互不影响,实现了松耦合。如果以后涂涂还想吃饺子,只需要实现Ifood接口即可。

接口分离原则(ISP-Interface Segregation Principle)

接口分离原则的核心思想:不应该强迫客户程序依赖他们不需要的方法。意思就是说:一个接口不需要不需要提供太多的行为,不应该把所有的操作都封装到一个接口中。

这里的接口不仅指interface定义的接口,包含以下两种:

  • java中声明的一个类,通过new关键字产生的一个实例,他是对一个类型的事务的描述,也是一种接口(Phone phone = new Phone())
  • 类接口,通过interface关键字定义好的接口

使用接口分离原则的规范:

  • 接口尽量小(主要是保证一个接口只服务于一个子模块或者业务逻辑)
  • 接口高内聚(对内高度依赖,对外尽可能隔离。即一个接口内部声明的方法相互之间都与某一个子模块相关,且是这个子模块必需的)
  • 接口设计时有限度的(如果完全遵循接口分离原则会是接口的力度越来越小,这样造成接口数量剧增,增加系统复杂性,所以这个没有固定标准,需要根据经验判断)
//定义美女接口
public interface IprettyGirl {void greatLooks();//长相好void greatFigure();//身材好void greatTemperament();//气质佳
}class PrettyGirl implements IprettyGirl{private String name;public PrettyGirl(String name) {this.name = name;}@Overridepublic void greatFigure() {System.out.println(name+":身材非常好");}@Overridepublic void greatLooks() {System.out.println(name+":长相非常好");}@Overridepublic void greatTemperament() {System.out.println(name+":气质非常好");}
}//抽象一个帅哥
abstract class IMan{protected IprettyGirl prettyGirl;public IMan(IprettyGirl prettyGirl) {this.prettyGirl = prettyGirl;}//帅哥开始找美女了public abstract void findGirl();
}class Man extends IMan{public Man(IprettyGirl prettyGirl) {super(prettyGirl);}@Overridepublic void findGirl() {System.out.println("找到美女了。。。");atFigure();atLooks();atTemperament();}
}class Beijing{//在北京找美女public static void main(String[] args){IprettyGirl jiajai = new PrettyGirl("佳佳");IMan man = new Man(jiajai);man.findGirl();}
}

这里有个问题是,接口的划分不是很清晰,有的人认为长相好,身材好的就是美女,有的则认为气质佳的就是美女,所以需要把接口划分的再细致一点,长相好身材好的为一个接口,气质佳的为一个接口,以满足不同人的审美观。

迪米特原则(LOD-Law of Demeter)

迪米特原则的核心思想是:一个对象应该对其他对象尽可能少的了解。即实现对象之间解耦,弱耦合。例如除了探亲,监狱(类)里的犯人(类内部信息)不应该与外界有接触。他们与外界信息传递是通过狱警(迪米特法则的执行者)来执行。

例如:家人去监狱探亲,对于家人来说只与某个犯人是亲人,家人与其他犯人之间并不认识。家人与监狱之间就是弱耦合。

开闭原则(OCP-Open for Extension,Closed for Modification)

开闭原则的核心思想:一个对象对扩展开放,对修改关闭。意思是说对类的改动是通过增加代码进行的,而不是改动现有的代码。这就需要借助于抽象和多态,把可能变化的内容抽象出来,从而使抽象的部分相对稳定,具体的实现是可以改变和扩展的。

尽量使用对象组合,而不是对象继承

用一个示例来说明对象的继承与组合:

假如有对象A,实现了a1方法,对象C想扩展A的功能,并给它增加一个新的c11,两种实现方案

继承

class A{public void a1(){System.out.println("now in A.a1");}
}class C extends A{public void c11(){System.out.println("now in C.c11");}
}

对象组合

class C2{//创建A对象的实例A a = new A();public void a1(){// 转调A对象的功能a.a1();}public void c11(){System.out.println("now in C2.c11");}
}

对象组合优点:

  • 可以由选择的复用功能,不是所有A的功能都会被复用;
  • 在调转前后可以实现一些功能处理,并且A对象并不知道在调用a1方法的时候被添加了功能;
  • 可以组合更多对象(Java不支持多继承)

 

摘自:修炼—清华大学出版社,于广编著

本文发布于:2024-01-29 13:01:21,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170650448315467.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:架构   原则
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23