面向对象编程的设计原则

十年热恋 提交于 2019-12-08 23:07:33

 

面向对象的基本设计原则(SOLID)

1.单一职能原则(SRP-Single responsibility principle)

  • 描述
    • 一个类、接口或方法只负责一项职责(只有一个改变的原因)
  • 优点
    • 可以降低类的复杂度、类之间的耦合度
    • 便于扩展、复用和维护,提高代码可读性
    • 降低变更引起的风险
  • 应用
    • Entity、Biz、Dao、Control
  • 说明
1.一般通过多接口(Interface)的形式进行职能划分2.要注意职能扩散

2.开放-封闭原则(OCP-Open Closed Principle)

  • 描述
    • 类、模块等对于扩展是开放的(Open for extension)
    • 对于修改是封闭的(Closed for modification)

 

Software entities like classes,modules and functions should be open for extension but closed for modifications.

 

  • 分类
    • 梅耶开闭原则(类的继承)
    • 多态开闭原则(抽象化接口实现)
  • 优点
    • 增强系统的稳定性、可扩展性、可维护性
    • 提高代码的复用性
  •    说明

1.通过接口和抽象类对扩展进行约束

2.引用对象、参数类型尽量使用接口或抽象类

3.接口或抽象类尽量不修改(可通过增加新的接口或抽象类完成扩展)

4.使用元数据控制模块(例如Spring的控制反转)

  • 代码示例
     1 import java.text.NumberFormat;
     2 
     3 /** 开闭原则-扩展开放,修改关闭 */
     4 public class OCPTest {
     5     public static void main(String[] args){
     6         String bookName = "一本好书";
     7         Integer bookPrice = 1000;//价格保留两位小数,处理时扩大100倍,显示时再缩小100倍
     8         IProduct product = new Product(bookName,bookPrice);
     9         IProduct offProduct = new OffProduct(bookName,bookPrice);
    10 
    11         NumberFormat format = NumberFormat.getCurrencyInstance();
    12         format.setMaximumFractionDigits(2);//保留两位小数
    13 
    14         System.out.println("打折前:\n图书名称:" + product.name() + "\t图书价格" + format.format(product.price() / 100.0));
    15         System.out.println("打折后:\n图书名称:" + offProduct.name() + "\t图书价格" + format.format(offProduct.price() / 100.0));
    16     }
    17 }
    18 
    19 /** 商品接口 */
    20 interface IProduct{
    21     String name();//商品名称
    22 
    23     int price();//商品价格
    24 }
    25 
    26 /** 商品类 */
    27 class Product implements IProduct{
    28     private String name;
    29     private Integer price;
    30 
    31     public Product(){}
    32 
    33     public Product(String name,Integer price){
    34         this.name = name;
    35         this.price = price;
    36     }
    37 
    38     @Override
    39     public String name(){
    40         return name;
    41     }
    42 
    43     @Override
    44     public int price(){
    45         //价格保留两位小数,处理时扩大100倍,显示时再缩小100倍
    46         return price;
    47     }
    48 }
    49 
    50 /** 打折商品,通过方法覆盖(扩展)实现修改 */
    51 class OffProduct extends Product{
    52     public OffProduct(String name,Integer price){
    53         super(name,price);
    54     }
    55 
    56     @Override
    57     public int price(){
    58         //打九折
    59         return super.price() * 90 / 100;
    60     }
    61 }
    View Code

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

  • 描述
    • 子类型必须能够透明地替换掉它的父类型(把父类都替换成它的子类,程序的行为没有变化)

 

1.If for each object o1 of type S there is an object o2 of type T
such that for all programs P defined in terms of T,
the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.
2.Functions that use pointers or references to base classes
must be able to use objects of derived classes without knowing it.

 

  • 优点
    • 提高系统的复用性、可扩展性  
    • 提高系统的健壮性、便于维护升级
  • 说明
继承并且覆盖超类方法的时候,
子类中的方法的可见性必须等于或者大于超类中的方法的可见性,
子类中的方法所抛出的受检异常只能是超类中对应方法所抛出的受检异常的子类
子类中的方法的返回值也可以是对应的超类方法的返回值的子类子类的方法的前置条件(参数类型)必须与超类中被复写的方法的前置条件相同或更宽松

4.接口隔离原则(ISP-Interface Segregation Principle)

  • 描述
    • 客户端不应该依赖它不需要的接口
    • 一个类对另一个类的依赖应该建立在最小的接口上

 

1.Clients should not be forced to depend upon interface that they don't use.
2.The dependency of one class to another one should depend on the smallest possible interface.

 

  • 方法

    1.根据需求进行接口拆分
    2.尽量降低接口、方法的访问权限
    3.只向外提供对方需要的方法

  • 优点
    • 增强系统的安全性
  • 代码示例
     1 /** 接口隔离原则-接口拆分、按需选择*/
     2 public class ISPTest {
     3     public static void main(String[] args){
     4        WheatenFood noodle = new WheatenFood("Noodle","Yummy","starch","big");
     5        noodle.show();
     6 
     7         FruitFood apple = new FruitFood("Apple","Yummy","vitamin");
     8         apple.show();
     9     }
    10 }
    11 
    12 /** 食物味道、营养 */
    13 interface IFoodTasteNutrition{
    14     void taste();
    15 
    16     void nutrition();
    17 }
    18 
    19 /** 装食物的碗的大小 */
    20 interface IFoodBowlSize{
    21     void bowlSize();
    22 }
    23 
    24 /** 面食 */
    25 class WheatenFood implements IFoodTasteNutrition,IFoodBowlSize{
    26     private String name;
    27     private String taste;
    28     private String nutrition;
    29     private String bowlSize;
    30 
    31     public WheatenFood(String name,String taste,String nutrition,String bowlSize){
    32         this.name = name;
    33         this.taste = taste;
    34         this.nutrition = nutrition;
    35         this.bowlSize = bowlSize;
    36     }
    37 
    38     @Override
    39     public void taste() {
    40         System.out.println(name + " tastes " + taste);
    41     }
    42 
    43     @Override
    44     public void nutrition() {
    45         System.out.println(name + " has much " + nutrition);
    46     }
    47 
    48     @Override
    49     public void bowlSize() {
    50         System.out.println(name + " is in a " + bowlSize + " bowl.");
    51     }
    52 
    53     public void show(){
    54         taste();
    55         nutrition();
    56         bowlSize();
    57     }
    58 }
    59 
    60 /** 水果 */
    61 class FruitFood implements IFoodTasteNutrition{
    62     private String name;
    63     private String taste;
    64     private String nutrition;
    65 
    66     public FruitFood(String name,String taste,String nutrition){
    67         this.name = name;
    68         this.taste = taste;
    69         this.nutrition = nutrition;
    70     }
    71 
    72     @Override
    73     public void taste() {
    74         System.out.println(name + " tastes " + taste);
    75     }
    76 
    77     @Override
    78     public void nutrition() {
    79         System.out.println(name + " has much " + nutrition);
    80     }
    81 
    82     public void show(){
    83         taste();
    84         nutrition();
    85     }
    86 }
    View Code

5.依赖倒转原则(DIP-Dependence Inversion Principle)

  • 描述:抽象(模块,接口或抽象类)不应该依赖细节(具体实现),细节应该依赖抽象。

    High level modules should not depend upon low level modules.
    Both should depend upon abstractions.
    Abstractions should not depend upon details.
    Details should depend upon abstractions.

  • 优点
    • 便于扩展和复用
    • 降低代码耦合度
    • 提高系统稳定性
  • 代码示例
     1 /** 依赖倒置原则-依赖于抽象类 */
     2 public class DIPTest {
     3     public static void main(String[] args){
     4         //狗的行为
     5         Animal dog = new Dog();
     6         AnimalBehavior dogBehavior = new AnimalBehavior(dog);
     7         dogBehavior.EatWhat();
     8         dogBehavior.HowToVoice();
     9         dogBehavior.WhereToSleep();
    10 
    11         //猫的行为
    12         Animal cat = new Cat();
    13         AnimalBehavior catBehavior = new AnimalBehavior(cat);
    14         catBehavior.EatWhat();
    15         catBehavior.HowToVoice();
    16         catBehavior.WhereToSleep();
    17     }
    18 }
    19 
    20 /** 接口确定动物基本行为 */
    21 abstract class Animal {
    22     protected String name = this.getClass().getName();
    23 
    24     abstract void  eat();
    25     abstract void voice();
    26     abstract void sleep();
    27 }
    28 
    29 /** 狗 */
    30 class Dog extends Animal {
    31     @Override
    32     public void eat() {
    33         System.out.println( name+ " love meat.");
    34     }
    35 
    36     @Override
    37     public void voice() {
    38         System.out.println(name + " Barking...");
    39     }
    40 
    41     @Override
    42     public void sleep() {
    43         System.out.println(name + " Lying on the floor.");
    44     }
    45 }
    46 
    47 /** 猫 */
    48 class Cat extends Animal{
    49     @Override
    50     public void eat() {
    51         System.out.println(name + " love fish.");
    52     }
    53 
    54     @Override
    55     public void voice() {
    56         System.out.println(name + " Miaowing...");
    57     }
    58 
    59     @Override
    60     public void sleep() {
    61         System.out.println(name + " Lying in the bed.");
    62     }
    63 }
    64 
    65 /** 动物行为,依赖抽象类Animal */
    66 class AnimalBehavior{
    67     private Animal animal;
    68     //构造函数依赖
    69     public AnimalBehavior(Animal animal){
    70         this.animal = animal;
    71     }
    72 
    73     public void EatWhat(){
    74         animal.eat();
    75     }
    76 
    77     public void HowToVoice(){
    78         animal.voice();
    79     }
    80 
    81     public void WhereToSleep(){
    82        animal.sleep();
    83     }
    84 }
    View Code

6.最少知识原则(LKP-Least Knowledge Principle)(迪米特法则,LOD-Law of Demeter)

  • 描述
    • 类(模块)之间尽可能少的发生联系和作用(只与直接的朋友类通信)

 

Only talk to your immedate friends.
朋友类:出现下成员变量、方法的输入输出参数中的类

 

  • 特点
    • 降低模块耦合度
    • 增加代码复杂度
  • 应用
    • 门面模式(Facade)
    • 中介模式(Mediator
  • 说明
    • 优先考虑将一个类设置成不变类
    • 尽量降低一个类的访问权限
    • 谨慎使用Serializable
    • 尽量降低成员的访问权限
  • 代码示例
     1 /** 迪米特法则-尽量提供最少信息 */
     2 public class LODTest {
     3     public static void main(String[] args){
     4         Student student = new Student("唐龙");
     5         HealthExamination exam = new HealthExamination(student);
     6         exam.examResult();
     7     }
     8 }
     9 
    10 /** 学生类 */
    11 class Student{
    12     private String name;//姓名
    13 
    14     public Student(String name){
    15         this.name = name;
    16     }
    17 
    18     public String getName() {
    19         return name;
    20     }
    21 
    22     public void setName(String name) {
    23         this.name = name;
    24     }
    25 }
    26 
    27 /** 体检类,基本测试对外不可见,只能查看最终测试结果 */
    28 class HealthExamination{
    29     private Student student;
    30 
    31     public HealthExamination(Student student){
    32         this.student = student;
    33     }
    34 
    35     private void bloodPressure(){
    36         System.out.println(student.getName() + "测血压");
    37     }
    38 
    39     private void eyeSight(){
    40         System.out.println(student.getName() + "测视力");
    41     }
    42 
    43     private void vitaCapacity(){
    44         System.out.println(student.getName() + "测肺活量");
    45     }
    46 
    47     public void examResult(){
    48         bloodPressure();
    49         eyeSight();
    50         vitaCapacity();
    51     }
    52 }
    View Code
  • 狭义的迪米特法则
  如果两个类不必彼此直接通信,
  那么这两个类就不应当发生直接的相互作用。
  如果其中的一个类需要调用另一个类的某一个方法的话,
  可以通过第三者转发这个调用。

其它

这些原则仅仅只作为设计时的参考,不一定要遵守,也不一定要全部遵守,需要根据具体场景进行合适的取舍。在千变万化中寻找不常变和不变,化繁为简,化疏为熟,万千法则,运用之妙,存乎一心。

*主要资料来源:《设计模式之禅》、网络

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!