首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java开发人员的SOLID设计原则简介

Java开发人员的SOLID设计原则简介

作者头像
曾高飞
发布2022-08-15 00:13:45
2050
发布2022-08-15 00:13:45
举报

  当你是软件工程的新手时,S.O.L.I.D.原则和设计模式是不容易理解或习惯的。我们都有问题,很难掌握SOLID+DP的思想,更难以正确实施。事实上,如何实现设计模式需要时间和大量实践。

  我可以说实话,关于SOLID设计模式以及TDD等其他领域,从本质上讲,它们很难教。以正确的方式把这些知识和信息传授给年轻人是非常困难的。

  在这篇文章中,我将以尽可能简单的方式,用简单易懂的例子来教授每SOLID字母。

  S代表SRP(单一责任原则)。基本思想是应用关注点分离,这意味着您应该尝试将关注点分离到不同的类中。一个类应该关注一个问题、一段逻辑或一个域。当域、规范或逻辑更改时,它应该只影响一个类。

  实施SRP前

  下面,我们违反了SRP。这堂课 VehicleServiceResource 实现了两种不同的事物,并以两种角色结束。如我们所见,这个类有两个注释来标记它的用法。

  一个是向客户端公开和服务HTTP端点车辆的角色。

  第二个是车辆服务的角色,它从存储区getVehicles()获取车辆,并计算总值calculateTotalValue():

  @EndPoint("vehicles")

  @Service

  public class VehicleServiceResource {

  …

  @GET

  public List getVehicles(){

  }

  public double calculateTotalValue(){}

  …

  }

  实现SRP的简单目标是将VehicleServiceResource分为两个不同的类:一个用于端点,另一个用于服务。

  我们所做的是将VehicleServiceResource类分成两个不同的类。

  VehiclerResource类只有一个,和一个作业。为了向客户机公开和服务HTTP资源载体,所有与业务逻辑相关的方法都指向VehicleService类。

  @EndPoint("vehicles")

  public class VehicleResource {

  @Service

  private VehicleService service;

  @GET

  public List getVehicles() {

  return this.service.getVehicles();

  }

  ...

  }

  我们创建了一个名为VehicleService的新类。这个类实现所有与车辆相关的逻辑。

  @Service

  public class VehicleService {

  ...

  public List getVehciles() {}

  public double calculateTotalValue(){}

  ...

  }

  O代表OCP(开闭原理)。开闭原则规定:

  “…软件实体,如模块、类、函数等,应为扩展而打开,但为修改而关闭。”

  术语“开放式扩展”意味着我们可以在代码中扩展和添加额外的案例/功能,而不改变或影响我们现有的实现。

  术语“修改关闭”意味着在添加额外的功能之后,我们不应该修改现有的实现。

  先试试对OCP的简单违反操作:

  public class VehicleValueCalculator {

  // lets assume a simple method to calculate the total value of a vehicle

  // with extra cost depending the type.

  public double calculateVehicle(Vehicle v){

  double value=0;

  if(v instanceof Car){

  value=v.getValue() + 2.0;

  } else if(v instanceof MotorBike) {

  value=v.getValue() + 0.4;

  }

  return value;

  }

  }

  当我们要包括一种新型车辆“卡车”时,就会违反OCP。需要对CalculateHicle方法进行重构和代码修改。

  public interface IVehicle {

  double calculateVehicle();

  }

  public class Car implements IVehicle {

  @Override

  public double calculateVehicle() {

  return this.getValue() + 2.0;

  }

  }

  public class MotorBike implements IVehicle {

  @Override

  public double calculateVehicle() {

  return this.getValue() + 0.4;

  }

  }

  public class Truck implements IVehicle {

  @Override

  public double calculateVehicle() {

  return this.getValue() + 3.4;

  }

  }

  这样,通过具有接受的方法,IVehicle以后在每次添加新型车辆时都无需进行重构/代码修改。

  public class Main {

  public static void main(String[] args){

  IVehicle car=new Car();

  IVhecile motorBike=new MotorBike();

  //new addition

  IVhecile truck=new Truck();

  double carValue=getVehicleValue(car);

  double motorBikeValue=getVehicleValue(motorBike);

  double truckValue=getVehicleValue(truck);

  }

  public double getVehicleValue(IVehicle v) {

  return v.calculateVehicle();

  }

  }

  L代表LSP(里氏替换原则):

  为了使这篇文章成为SOLID的介绍,而不会引起混淆,我将尝试使LSP尽可能简单,并排除很多具体的细节。

  LSP声明,当我们用任何子类型替换父类型时,软件不应更改所需的结果。

  LSP更多的是一个问题定义,而不是一个设计模式,以及我们可以做些什么来防止不良影响。

  为了更清楚地说明这一点,我们将查看下面的简单示例:

  /**

  * The Base Rectangle class

  * This class defines the structure and properties of all types of rectangles

  */

  public class Rectangle {

  private int width;

  private int height;

  public Rectangle(){}

  public Rectangle(int w,int h) {

  this.width=w;

  this.height=h;

  }

  public int getWidth() {

  return width;

  }

  public void setWidth(int width) {

  this.width=width;

  }

  public int getHeight() {

  return height;

  }

  public void setHeight(int height) {

  this.height=height;

  }

  public int getArea() {

  return this.height * this.width;

  }

  /**

  * LSP violation is case of a Square reference.

  */

  public final static void setDimensions(Rectangle r,int w,int h) {

  r.setWidth(w);

  r.setHeight(h);

  //assert r.getArea()==https://www.ershouyi.com/ * h

  }

  }

  /**

  * A Special kind of Rectangle

  */

  public class Square extends Rectangle {

  @Override

  public void setHeight(int h){

  super.setHeight(h);

  super.setWidth(h);

  }

  @Override

  public void setWidth(int w) {

  super.setWidth(w);

  super.setHeight(w);

  }

  }

  在谈到LSP时,我们在Rectangle类中有setDimensions方法,它接受一种矩形对象并设置宽度和高度。这是一种违规行为,因为行为发生了变化,并且在传递平方引用时数据不一致。

  有很多解决办法。其中有些是采用开闭原则,通过合同模式进行设计。

  还有很多其他的解决LSP冲突的方法,但是我不打算在这里解释,因为这超出了本文的范围。

  I代表ISP(接口隔离原则)。界面分离原则由罗伯特C马丁在为施乐咨询时定义。他将其定义为:

  “不应强迫客户端依赖于它们不使用的接口。”

  ISP说我们应该把我们的接口分成更小更具体的接口。

  下面是表示两个不同角色的接口的示例。一个是处理连接的角色,比如打开和关闭,另一个是发送和接收数据。

  public interface Connection {

  void open();

  void close();

  byte[] receive();

  void send(byte[] data);

  }

  应用ISP之后,我们得到了两个不同的接口,每个接口代表一个确切的角色。

  public interface Channel {

  byte[] receive();

  void send(byte[] data);

  }

  public interface Connection {

  void open();

  void close();

  }

  d代表DIP(依赖倒置原则)。DIP声明我们应该依赖抽象(接口和抽象类),而不是具体的实现(类)。

  接下来是违反DIP。我们有一个电子邮件类,具体取决于一个直接拼写检查器类:

  public class Emailer{

  private SpellChecker spellChecker;

  public Emailer(SpellChecker sc) {

  this.spellChecker=sc;

  }

  public void checkEmail() {

  this.spellChecker.check();

  }

  }

  拼写检查器类:

  public class SpellChecker {

  public void check() throws SpellFormatException {

  }

  }

  它现在可能可以工作,但是过了一段时间,我们有两个不同的拼写检查实现,我们想包括在内。我们有默认的拼写检查器和一个新的希腊拼写检查器。

  对于当前的实现,需要重构,因为Emailer类只使用SpellChecker类。

  一个简单的解决方案是为要实现的不同的拼写检查器创建接口。

  // The interface to be implemented by any new spell checker.

  public interface ISpellChecker {

  void check() throws SpellFormatException;

  }

  现在,Emailer类只接受构造函数上的ISpellChecker引用。下面,我们将Emailer类更改为不关心/依赖实现(具体类),而是依赖接口(ISpellChecker)

  public class Emailer{

  private ISpellChecker spellChecker;

  public Emailer(ISpellChecker sc) {

  this.spellChecker=sc;

  }

  public void checkEmail() {

  this.spellChecker.check();

  }

  }

  实现方面:

  public class SpellChecker implements ISpellChecker {

  @Override

  public void check() throws SpellFormatException {

  }

  }

  public class GreekSpellChecker implements ISpellChecker {

  @Override

  public void check() throws SpellFormatException {

  }

  }

  下面是另一个代码示例。我们正在将ISpellChecker类型传递给Emailer构造函数-不管实现是什么。

  public static class Main{

  public static void main(String[] a) {

  ISpellChecker defaultChecker=new SpellChecker();

  ISpellChecker greekChecker=new GreekSpellChecker();

  new Emailer(defaultChecker).checkEmail();

  new Emailer(greekChecker).checkEmail();

  }

  }

  就这样!给你!我们希望您喜欢Java代码中SOLID设计原则的简单概述。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档