看看这篇针对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. Typescript I: 遍历Array的方法:for, forEach, every等

    Typescript的官方文档 Iterators and Geneators (https://www.typescriptlang.org/docs/handbook/iterators-and- ...

  2. JavaScript with Image:创建缩略图

    当图片很大,直接把图片从Server下载到浏览器上看是一种很不明智的做法,浪费了服务器的资源,网络带宽和客户端的资源.所以,通常Server和Client之间会传输缩略图,只有当Client请求某张图 ...

  3. vue项目iframe的传值问题

    前言 项目需要,我需要引入一个已经封装好的浏览器插件.插件只能以html的方式调用, 所以.我把插件的使用封装了一个html页面.vue项目则利用iframe的方式引入. 到这里我就遇到了一个问题,那 ...

  4. beyong Compare4解决30天的评估期结束

    刚开始是删掉注册表的CacheId(无效) 1.在搜索栏中输入 regedit ,打开注册表2.删除项目CacheId :HKEY_CURRENT_USER\Software\Scooter Soft ...

  5. 一分钟带你了解下MyBatis的动态SQL!

    MyBatis的强大特性之一便是它的动态SQL,以前拼接的时候需要注意的空格.列表最后的逗号等,现在都可以不用手动处理了,MyBatis采用功能强大的基于OGNL的表达式来实现,下面主要介绍下. 一. ...

  6. Java :一文掌握 Lambda 表达式

    本文将介绍 Java 8 新增的 Lambda 表达式,包括 Lambda 表达式的常见用法以及方法引用的用法,并对 Lambda 表达式的原理进行分析,最后对 Lambda 表达式的优缺点进行一个总 ...

  7. Tarjan-割点

    割点——tarjan #include <bits/stdc++.h> using namespace std; ; ; int n, m; int ans;//个数 * MAXM], n ...

  8. 使用 Rsync 从 Windows 同步数据到 Linux

    为什么要使用 rsync 从 Windows 到 linux 进行同步? 我们经常会面临这种的情况,项目使用 Windows 开发,最终部署在 Linux 上,但有时想要进行测试.维护.迭代版本时操作 ...

  9. 带你涨姿势的认识一下 Kafka 消费者

    之前我们介绍过了 Kafka 整体架构,Kafka 生产者,Kafka 生产的消息最终流向哪里呢?当然是需要消费了,要不只产生一系列数据没有任何作用啊,如果把 Kafka 比作餐厅的话,那么生产者就是 ...

  10. Dart Learn Notes 04

    流程控制语句 流程控制语句的作用就是控制代码的执行流程. if and else var a = 10; if(a > 10){ print('ok'); }else if( 5 < a ...