看看这篇针对Java开发人员的SOLID设计原则简介。抽丝剥茧,细说架构那些事——【优锐课】设计模式
当你刚接触软件工程时,这些原理和设计模式不容易理解或习惯。咱们都遇到了问题,很难理解SOLID + DP的思想,甚至很难正确实施它们。确实,“为何要SOLID?”的整个概念,以及如何实施设计模式,这须要时间和大量实践。架构
我能够说实话,关于SOLID设计模式以及TDD等其余领域,从本质上讲,它们很难教。很难以正确的方式将全部这些知识和信息传授给年轻人。ide
在本文中,我将以尽量简单的术语,经过简单易懂的示例来教授SOLID的每一个字母。函数
S表明SRP(单一责任原则)。基本思想是应用关注点分离,这意味着你应尝试将关注点分离到不一样的类中。一堂课应该专一于单个问题,逻辑或单个领域。当域,规范或逻辑发生变化时,它只影响一个类。工具
下面,咱们违反了SRP。VehicleServiceResource
类实现了两种不一样的方法,并以两种角色结束。如咱们所见,该类具备两个标记其用法的注释。this
一种是向客户端公开和提供HTTP终结点服务的角色。spa
第二个是车辆服务的角色,该服务从存储getVehicles()
中获取车辆并计算总值calculateTotalValue()
:设计
1 @EndPoint("vehicles") 2 @Service 3 public class VehicleServiceResource { 4 … 5 @GET 6 public List getVehicles(){ 7 } 8 public double calculateTotalValue(){} 9 … 10 }
实现SRP的简单目标是将VehicleServiceResource
分为两个不一样的类:一个用于端点,另外一个用于服务。code
咱们要作的是获取VehicleServiceResource
类,并将其分为两个不一样的类。orm
VehicleResource
类仅具备一项和一项工做。为了向客户端公开HTTP资源工具并向其提供服务,全部与业务逻辑相关的方法均致使VehicleService
类。
1 @EndPoint("vehicles") 2 public class VehicleResource { 3 @Service 4 private VehicleService service; 5 @GET 6 public List getVehicles() { 7 return this.service.getVehicles(); 8 } 9 ... 10 }
咱们建立了一个名为 VehicleService
的新类。此类实现全部与车辆有关的逻辑。
1 @Service 2 public class VehicleService { 3 ... 4 public List getVehciles() {} 5 public double calculateTotalValue(){} 6 ... 7 }
O表明OCP(开闭原理)。开闭原则指出:
" ... 软件实体(例如模块,类,功能等)应打开以进行扩展,但应关闭以进行修改。"
术语“开放扩展”是指咱们能够在代码中扩展并包括额外的案例/功能,而不会更改或影响咱们现有的实现。
术语“关闭以进行修改”表示在添加了附加功能以后,咱们不该修改现有的实现。
一个简单的违反OCP的行为:
1 public class VehicleValueCalculator { 2 // lets assume a simple method to calculate the total value of a vehicle 3 // with extra cost depending the type. 4 public double calculateVehicle(Vehicle v){ 5 double value = 0; 6 if(v instanceof Car){ 7 value = v.getValue() + 2.0; 8 } else if(v instanceof MotorBike) { 9 value = v.getValue() + 0.4; 10 } 11 return value; 12 } 13 }
当咱们要包括一种新型车辆“卡车”时,就会违反OCP。须要对calculateVehicle
方法进行重构和代码修改。
1 public interface IVehicle { 2 double calculateVehicle(); 3 } 4 public class Car implements IVehicle { 5 @Override 6 public double calculateVehicle() { 7 return this.getValue() + 2.0; 8 } 9 } 10 public class MotorBike implements IVehicle { 11 @Override 12 public double calculateVehicle() { 13 return this.getValue() + 0.4; 14 } 15 }
咱们的新卡车
1 public class Truck implements IVehicle { 2 @Override 3 public double calculateVehicle() { 4 return this.getValue() + 3.4; 5 } 6 }
这样,经过使用一种接受IVehicle的方法,之后在每次添加新型车辆时都无需进行重构/代码修改。
范例程式码
1 public class Main { 2 public static void main(String[] args){ 3 IVehicle car = new Car(); 4 IVhecile motorBike = new MotorBike(); 5 //new addition 6 IVhecile truck = new Truck(); 7 double carValue = getVehicleValue(car); 8 double motorBikeValue = getVehicleValue(motorBike); 9 double truckValue = getVehicleValue(truck); 10 } 11 public double getVehicleValue(IVehicle v) { 12 return v.calculateVehicle(); 13 } 14 }
L表明LSP(Liskov替代原理):
为了使这篇文章成为SOLID的介绍,而不会引发混淆,我将尝试使LSP尽量简单,并排除不少具体的细节,由于LSP又是另外一天的讨论和辩论。
LSP指出,当咱们用任何子类型替换父类型时,该软件不该改变指望的结果。
LSP不只仅是一个设计模式,更是一个问题定义,而咱们能够作的是防止不良影响。
为了更清楚地说明这一点,咱们将检查如下简单示例:
1 /** 2 * The Base Rectangle class 3 * This class defines the structure and properties of all types of rectangles 4 */ 5 public class Rectangle { 6 private int width; 7 private int height; 8 public Rectangle(){} 9 public Rectangle(int w,int h) { 10 this.width = w; 11 this.height = h; 12 } 13 public int getWidth() { 14 return width; 15 } 16 public void setWidth(int width) { 17 this.width = width; 18 } 19 public int getHeight() { 20 return height; 21 } 22 public void setHeight(int height) { 23 this.height = height; 24 } 25 public int getArea() { 26 return this.height * this.width; 27 } 28 /** 29 * LSP violation is case of a Square reference. 30 */ 31 public final static void setDimensions(Rectangle r,int w,int h) { 32 r.setWidth(w); 33 r.setHeight(h); 34 //assert r.getArea() == w * h 35 } 36 }
1 /** 2 * A Special kind of Rectangle 3 */ 4 public class Square extends Rectangle { 5 @Override 6 public void setHeight(int h){ 7 super.setHeight(h); 8 super.setWidth(h); 9 } 10 @Override 11 public void setWidth(int w) { 12 super.setWidth(w); 13 super.setHeight(w); 14 } 15 }
在谈论LSP时,咱们在Rectangle类中有setDimensions方法,该方法接受Rectangle对象的类型并设置宽度和高度。这是违规的,由于行为发生了变化,而且在传递方形引用时咱们的数据不一致。
有不少解决方案。其中一些将应用“开放式封闭原则”和经过“合同”模式进行设计。
还有许多其余解决LSP违规问题的方法,可是在此不作解释,由于它不在本文讨论范围以内。
I表明ISP(接口隔离原理)。接口隔离原则是由Robert C. Martin在为Xerox咨询时定义的。他将其定义为:
“不该强迫客户依赖他们不使用的接口。”
ISP指出,咱们应该将接口拆分为更小,更具体的接口。
如下是表明两个不一样角色的界面示例。一个角色是处理打开和关闭之类的链接,另外一个角色是发送和接收数据。
1 public interface Connection { 2 void open(); 3 void close(); 4 byte[] receive(); 5 void send(byte[] data); 6 }
在应用ISP以后,咱们获得了两个不一样的接口,每一个接口表明一个确切的角色。
1 public interface Channel { 2 byte[] receive(); 3 void send(byte[] data); 4 } 5 public interface Connection { 6 void open(); 7 void close(); 8 }
D表明DIP(依赖性反转原理)。DIP声明咱们应该依赖抽象(接口和抽象类),而不是具体的实现(类)。
接下来是违反DIP。咱们有一个Emailer
类,具体取决于直接的SpellChecker
类:
1 public class Emailer{ 2 private SpellChecker spellChecker; 3 public Emailer(SpellChecker sc) { 4 this.spellChecker = sc; 5 } 6 public void checkEmail() { 7 this.spellChecker.check(); 8 } 9 }
Spellchecker
类:
1 public class SpellChecker { 2 public void check() throws SpellFormatException { 3 } 4 }
目前可能可使用,可是过了一下子,咱们要包含两种不一样的SpellChecker
实现。咱们有默认的SpellChecker
和新greek spellchecker
。
在当前的实现中,须要重构,由于Emailer类仅使用SpellChecker
类。
一个简单的解决方案是为不一样的SpellChecker
建立要实现的接口。
1 // The interface to be implemented by any new spell checker. 2 public interface ISpellChecker { 3 void check() throws SpellFormatException; 4 }
如今,Emailer
类在构造函数上仅接受ISpellChecker
引用。下面,咱们将Emailer
类更改成不关心/不依赖于实现(具体的类),而是依赖于接口(ISpellChecker
)
1 public class Emailer{ 2 private ISpellChecker spellChecker; 3 public Emailer(ISpellChecker sc) { 4 this.spellChecker = sc; 5 } 6 public void checkEmail() { 7 this.spellChecker.check(); 8 } 9 }
咱们为ISpellChecker
提供了许多实现:
1 public class SpellChecker implements ISpellChecker { 2 @Override 3 public void check() throws SpellFormatException { 4 } 5 } 6 public class GreekSpellChecker implements ISpellChecker { 7 @Override 8 public void check() throws SpellFormatException { 9 } 10 }
这是另外一个代码示例。不管实现是什么,咱们都将ISpellChecker
类型传递给Emailer构造函数。
1 public static class Main{ 2 public static void main(String[] a) { 3 ISpellChecker defaultChecker = new SpellChecker(); 4 ISpellChecker greekChecker = new GreekSpellChecker(); 5 new Emailer(defaultChecker).checkEmail(); 6 new Emailer(greekChecker).checkEmail(); 7 } 8 }
就是这样!但愿你喜欢Java代码中SOLID设计原理的简单概述。