看看这篇针对Java开发人员的SOLID设计原则简介。抽丝剥茧,细说架构那些事——【优锐课】

当你刚接触软件工程时,这些原理和设计模式不容易理解或习惯。我们都遇到了问题,很难理解SOLID + DP的思想,甚至很难正确实施它们。确实,“为什么要SOLID?”的整个概念,以及如何实施设计模式,这需要时间和大量实践。

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

让SOLID 变得容易

在本文中,我将以尽可能简单的术语,通过简单易懂的示例来教授SOLID的每个字母。

SOLID的“S”

S代表SRP(单一责任原则)。基本思想是应用关注点分离,这意味着你应尝试将关注点分离到不同的类中。一堂课应该专注于单个问题,逻辑或单个领域。当域,规范或逻辑发生变化时,它只影响一个类。

实施SRP之前

下面,我们违反了SRP。VehicleServiceResource类实现了两种不同的方法,并以两种角色结束。如我们所见,该类具有两个标记其用法的注释。

一种是向客户端公开和提供HTTP终结点服务的角色。

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

 @EndPoint("vehicles")
@Service
public class VehicleServiceResource {

@GET
public List getVehicles(){
}
public double calculateTotalValue(){}

}

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

SRP实施后

我们要做的是获取VehicleServiceResource类,并将其分为两个不同的类。

VehicleResource类仅具有一项和一项工作。为了向客户端公开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(){}
...
}

SOLID的“O”

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。需要对calculateVehicle方法进行重构和代码修改。

解决

 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();
}
}

SOLID的“L”

L代表LSP(Liskov替代原理):

为了使这篇文章成为SOLID的介绍,而不会引起混淆,我将尝试使LSP尽可能简单,并排除很多具体的细节,因为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() == w * 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方法,该方法接受Rectangle对象的类型并设置宽度和高度。这是违规的,因为行为发生了变化,并且在传递方形引用时我们的数据不一致。

有很多解决方案。其中一些将应用“开放式封闭原则”和通过“合同”模式进行设计。

还有许多其他解决LSP违规问题的方法,但是在此不做解释,因为它不在本文讨论范围之内。

SOLID的“I”

I代表ISP(接口隔离原理)。接口隔离原则是由Robert C. Martin在为Xerox咨询时定义的。他将其定义为:

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

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();
}

SOLID的“D”

D代表DIP(依赖性反转原理)。DIP声明我们应该依赖抽象(接口和抽象类),而不是具体的实现(类)。

接下来是违反DIP。我们有一个Emailer类,具体取决于直接的SpellChecker类:

 public class Emailer{
private SpellChecker spellChecker;
public Emailer(SpellChecker sc) {
this.spellChecker = sc;
}
public void checkEmail() {
this.spellChecker.check();
}
}

Spellchecker类:

 public class SpellChecker {
public void check() throws SpellFormatException {
}
}

目前可能可以使用,但是过了一会儿,我们要包含两种不同的SpellChecker实现。我们有默认的SpellChecker和新greek spellchecker

在当前的实现中,需要重构,因为Emailer类仅使用SpellChecker类。

一个简单的解决方案是为不同的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();
}
}

我们为ISpellChecker提供了许多实现:

 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设计原理的简单概述。

适用于Java开发人员的SOLID设计原则简介的更多相关文章

  1. 11.IntelliJ IDEA详细配置和使用教程(适用于Java开发人员)

    转自:https://blog.csdn.net/chssheng2007/article/details/79638076 前言 正所谓工欲善其事必先利其器,对开发人员而言若想提高编码效率,一款高效 ...

  2. IntelliJ IDEA详细配置和使用教程(适用于Java开发人员)

    关闭Intellij IDEA自动更新在File->Settings->Appearance & Behavior->System Settings->Updates下 ...

  3. 成为杰出Java开发人员的10个步骤

    在优锐课的学习分享中,讨论了如果你是Java开发人员并且对技术充满热情,则可以按照以下十个步骤进行操作,这可以使你成为杰出的Java开发人员. 1.具有扎实的基础和对OO原理的理解 对于Java开发人 ...

  4. 每个Java开发人员都应该知道的4个Spring注解

    这是每个Java开发人员都应该知道的最重要的Spring注解.感谢优锐课老师对本文提供的一些帮助. 随着越来越多的功能被打包到单个应用程序或一组应用程序中,现代应用程序的复杂性从未停止增长.尽管这种增 ...

  5. 工作那么久,才知道的 SOLID 设计原则

    认识 SOLID 原则 无论是软件系统设计,还是代码实现,遵循有效和明确的设计原则,都利于系统软件灵活可靠,安全快速的落地,更重要的是能灵活地应对需求,简化系统扩展和维护,避免无效的加班.本文主要讨论 ...

  6. Java开发人员最常犯的10个错误

    这个列表总结了10个Java开发人员最常犯的错误. Array转ArrayList 当需要把Array转成ArrayList的时候,开发人员经常这样做: List<String> list ...

  7. 面向 Java 开发人员的 Ajax: 构建动态的 Java 应用程序

    面向 Java 开发人员的 Ajax: 构建动态的 Java 应用程序 Ajax 为更好的 Web 应用程序铺平了道路 在 Web 应用程序开发中,页面重载循环是最大的一个使用障碍,对于 Java™ ...

  8. 每个Java开发人员都应该知道的10个基本工具

    大家好,我们已经在2019年的第9个月,我相信你们所有人已经在2019年学到了什么,以及如何实现这些目标.我一直在写一系列文章,为你提供一些关于你可以学习和改进的想法,以便在2019年成为一个更好的. ...

  9. 高级Java开发人员最常访问的几个网站

    这是高级Java开发人员最常访问的几个网站. 这些网站提供新闻,一般问题或面试问题的答案,精彩的讲座等.质量是优秀网站的关键因素,这此网站都有较高的质量内容.下面逐一介绍: 1. Stackoverf ...

随机推荐

  1. [LC]235题 二叉搜索树的最近公共祖先 (树)(递归)

    ①题目 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先. 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p.q,最近公共祖先表示为一个结点 x,满足 x 是 p.q 的祖先 ...

  2. 一个ip, 两个域名, 两个ssl, 访问多个不同的项目

    在前面一篇中说过, 入了好几个坑. 后来使用了nginx+tomcat配置的方式. 终于成功了. 因为头一次使用nginx, 不知道具体怎么操作, 于是我在操作的时候, 按照以下几个步骤执行的: 导航 ...

  3. nyoj 366 D的小L (全排列)

    D的小L 时间限制:4000 ms  |  内存限制:65535 KB 难度:2   描述       一天TC的匡匡找ACM的小L玩三国杀,但是这会小L忙着哩,不想和匡匡玩但又怕匡匡生气,这时小L给 ...

  4. hdu 2063 过山车 (二分图,最大匹配)

    过山车Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submissi ...

  5. nyoj 455-黑色帽子

    455-黑色帽子 内存限制:64MB 时间限制:1000ms 特判: No 通过数:4 提交数:7 难度:1 题目描述:         最近发现了一个搞笑的游戏,不过目前还没玩过.一个舞会上,每个人 ...

  6. Linux菜鸟——搭建虚拟机环境

    最近在学习Linux搭建服务器,但本人对于Linux一无所知,所以花了两天时间把安装,命令,控制,搭建环境等一系列操作都囫囵吞枣了一遍,但又怕自己记不住太多,所以记录下以前搭建的所有步骤,希望能提醒自 ...

  7. PostGIS 结合Openlayers以及Geoserver实现最短路径分析(二)

    前文讲述了怎么用ArcMap制作了测试数据,并导入了PostGIS,接下来我们需要结合PgRouting插件,对入库的数据再进行一下处理. 1.在pgAdmin中,执行下面的sql语句 --添加起点字 ...

  8. PowerMock学习(六)之Mock Final的使用

    Mock Final mockfinal相对来说就比较简单了,使用powermock来测试使用final修饰的method或class,比较简单,接口调用部分,还是service调用dao. 对于接口 ...

  9. 微信浏览器跳转浏览器下载app解决方案

    新版本微信浏览器中,已禁用下载APP应用,只支持打开微信合作商APP下载,所以无法通过微信浏览器直接下载APP应用.列举微信浏览器下载APP的种解决方案: 方案:通过Url 跳转到手机默认浏览器,或者 ...

  10. Java生鲜电商平台-生鲜售后系统的退款架构设计与代码分享

    Java生鲜电商平台-生鲜售后系统的退款架构设计与代码分享 说明:任何一个电商行业都涉及到退货与退款的问题,但是生鲜电商行业还设有一个显著的特点,那就是换货.在人性面前,各种各样的退货,退款,换货的售 ...