成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

優雅代碼的秘密,都藏在這六個設計原則中

開發 前端
高層模塊不應該依賴低層模塊,兩者都應該依賴其抽象;抽象不應該依賴細節,細節應該依賴抽象。它的核心思想是:要面向接口編程,而不要面向實現編程。

?前言

大家好,我是撿田螺的小男孩。

優雅的代碼,猶如亭亭玉立的美女,讓人賞心悅目。而糟糕的代碼,卻猶如屎山,讓人避而遠之。

如何寫出優雅的代碼呢?那就要理解并熟悉應用這6個設計原則啦:開閉原則、單一職責原則、接口隔離原則 、迪米特法則、里氏替換原則、依賴倒置原則。本文呢,將通過代碼demo,讓大家輕松理解這6個代碼設計原則,加油~

1. 開閉原則

開閉原則,即對擴展開放,對修改關閉。

對于擴展和修改,我們怎么去理解它呢?擴展開放表示,未來業務需求是變化萬千,代碼應該保持靈活的應變能力。修改關閉表示不允許在原來類修改,保持穩定性。

圖片

因為日常需求是不斷迭代更新的,所以我們經常需要在原來的代碼中修改。如果代碼設計得不好,擴展性不強,每次需求迭代,都要在原來代碼中修改,很可能會引入bug。因此,我們的代碼應該遵循開閉原則,也就是對擴展開放,對修改關閉。

為了方便大家理解開閉原則,我們來看個例子:假設有這樣的業務場景,大數據系統把文件推送過來,根據不同類型采取不同的解析方式。多數的小伙伴就會寫出以下的代碼:

if(type=="A"){
//按照A格式解析

}else if(type=="B"){
//按B格式解析
}else{
//按照默認格式解析
}

這段代碼有什么問題呢?

如果分支變多,這里的代碼就會變得臃腫,難以維護,可讀性低。

如果你需要接入一種新的解析類型,那只能在原有代碼上修改。

顯然,增加、刪除某個邏輯,都需要修改到原來類的代碼,這就違反了開閉原則了。為了解決這個問題,我們可以使用策略模式去優化它。

你可以先聲明一個文件解析的接口,如下:

public interface IFileStrategy {

//屬于哪種文件解析類型,A或者B
FileTypeResolveEnum gainFileType();

//封裝的公用算法(具體的解析方法)
void resolve(Object param);
}

然后實現不同策略的解析文件,如類型A解析:

@Component
public class AFileResolve implements IFileStrategy {

@Override
public FileTypeResolveEnum gainFileType() {
return FileTypeResolveEnum.File_A_RESOLVE;
}

@Override
public void resolve(Object objectparam) {
logger.info("A 類型解析文件,參數:{}",objectparam);
//A類型解析具體邏輯
}
}

如果未來需求變更的話,比如增加、刪除某個邏輯,不會再修改到原來的類啦,只需要修改對應的文件解析類型的類即可。

對于如何使用設計模式,大家有興趣的話,可以看我以前的這篇文章哈:實戰!工作中常用到哪些設計模式

2. 單一職責原則

單一職責原則:一個類或者一個接口,最好只負責一項職責。比如一個類C?違反了單一原則,它負責兩個職責P1和P2?。當職責P1?需要修改時,就會改動到類C?,這就可能導致原本正常的P2也受影響。

如何更好理解呢?比如你實現一個圖書管理系統,一個類既有圖書的增刪改查,又有讀者的增刪改查,你就可以認為這個類違反了單一原則。因為這個類涉及了不同的功能職責點,你可以把這個拆分。

圖片

以上圖書管理系統這個例子,違反單一原則,按業務拆分。這比較好理解,但是有時候,一個類并不是那么好區分。這時候大家可以看這個標準,來判斷功能職責是否單一:

  • 類中的私有方法過多
  • 你很難給類起一個合適的名字
  • 類中的代碼行數、函數或者屬性過多
  • 類中大量的方法都是集中操作類中的某幾個屬性
  • 類依賴的其他類過多,或者依賴類的其他類過多

比如,你寫了一個方法,這個方法包括了日期處理和借還書的業務操作,你就可以把日期處理抽到私有方法。再然后,如果你發現,很多私有方法,都是類似的日期處理,你就可以把這個日期處理方法抽成一個工具類。

日常開發中,單一原則的思想都有體現的。比如微服務拆分。

3. 接口隔離原則

接口隔離原則:接口的調用者或者使用者,不應該強迫依賴它不需要的接口。它要求建立單一的接口,不要建立龐大臃腫的接口,盡量細化接口,接口中的方法盡量少,讓接口中只包含客戶(調用者)感興趣的方法。即一個類對另一個類的依賴應該建立在最小的接口上。

比如類A?通過接口I?依賴類B?,類C?通過接口I?依賴類D?,如果接口I?對于類A?和類B?來說,都不是最小接口,則類B?和類D必須去實現他們不需要的方法。如下圖:

圖片

這個圖表達的意思是:類A?依賴接口I?中的method1、method2?,類B是對類A依賴的實現。類C依賴接口I?中的method1、method3,類D是對類C依賴的實現。對于實現類B和D,它們都存在用不到的方法,但是因為實現了接口I,所以必須要實現這些用不到的方法。

可以看下以下代碼:

public interface I {

void method1();

void method2();

void method3();
}

@Service
public class A {

@Resource(name="B")
private I i;

public void depend1() {
i.method1();
}

public void depend2(){
i.method2();
}

}

@Service("B")
public class B implements I {

@Override
public void method1() {
System.out.println("類B實現接口I的方法1");
}

@Override
public void method2() {
System.out.println("類B實現接口I的方法2");
}

//沒用到這個方法,但是也要默認實現,因為I有這個接口方法
@Override
public void method3() {

}
}

@Service
public class C {

@Resource(name="D")
private I i;

public void depend1(I i){
i.method1();
}

public void depend3(I i){
i.method3();
}

}

@Service("D")
public class D implements I {

@Override
public void method1() {
System.out.println("類D實現接口I的方法1");
}

//沒用到這個方法,但是也要默認實現,因為I有這個接口方法
@Override
public void method2() {

}

@Override
public void method3() {
System.out.println("類D實現接口I的方法3");
}
}

大家可以發現,如果接口過于臃腫,只要接口中出現的方法,不管對依賴于它的類有沒有用到,實現類都必須去實現這些方法。實現類B?沒用到method3?,它也要有個默認實現。實現類D?沒用到method2,它也要有個默認實現。

顯然,這不是一個好的設計,違反了接口隔離原則。我們可以對接口I進行拆分。拆分后的設計如圖2所示:

圖片

接口是不是分得越細越好呢?并不是。日常開發中,采用接口隔離原則對接口進行約束時,要注意以下幾點:

接口盡量小,但是要有限度。對接口進行細化可以提高程序設計靈活性是不掙的事實,但是如果過小,則會造成接口數量過多,使設計復雜化。所以一定要適度。

為依賴接口的類定制服務,只暴露給調用的類它需要的方法,它不需要的方法則隱藏起來。只有專注地為一個模塊提供定制服務,才能建立最小的依賴關系。

提高內聚,減少對外交互。使接口用最少的方法去完成最多的事情。運用接口隔離原則,一定要適度,接口設計的過大或過小都不好。設計接口的時候,只有多花些時間去思考和籌劃,才能準確地實踐這一原則。

4. 迪米特法則

定義:又叫最少知道原則。一個類對于其他類知道的越少越好,就是說一個對象應當對其他對象有盡可能少的了解,只和朋友談心,不和陌生人說話。它的核心思想就是,盡量降低類與類之間的耦合,盡最大能力減小代碼修改帶來的對原有的系統的影響。

比如一個生活例子:你對你的對象肯定了解的很多,但是如果你對別人的對象也了解很多,你的對象要是知道,那就要出大事了。

我們來看下一個違反迪米特法則的例子,業務場景是這樣的:一個學校,要求打印出所有師生的ID。

//學生  
class Student{
private String id;
public void setId(String id){
this.id = id;
}
public String getId(){
return id;
}
}

//老師
class Teacher{
private String id;
public void setId(String id){
this.id = id;
}
public String getId(){
return id;
}
}

//管理者(班長)
public class Monitor {

//所有學生
public List<Student> getAllStudent(){
List<Student> list = new ArrayList<Student>();
for(int i=0; i<100; i++){
Student student = new Student();
//為每個學生分配個ID
student.setId("學生Id:"+i);
list.add(student);
}
return list;
}

}

//校長
public class Principal {

//所有教師
public List<Teacher> getAllTeacher(){
List<Teacher> list = new ArrayList<Teacher>();
for(int i=0; i<20; i++){
Teacher emp = new Teacher();
//為全校老師按順序分配一個ID
emp.setId("老師編號"+i);
list.add(emp);
}
return list;
}

//所有師生
public void printAllTeacherAndStudent(ClassMonitor classMonitor) {

List<Student> list1 = classMonitor.getAllStudent();
for (Student s : list1) {
System.out.println(s.getId());
}

List<Teacher> list2 = this.getAllTeacher();
for (Teacher teacher : list2) {
System.out.println(teacher.getId());
}
}
}

這塊代碼。問題出在類Principal?中,根據迪米特法則,只能與直接的朋友發生通信,而Student?類并不是Principal?類的直接朋友(以局部變量出現的耦合不屬于直接朋友),從邏輯上講校長Principal?只與管理者Monitor?耦合就行了,可以讓Principal?繼承類Monitor?,重寫一個printMember的方法。優化后的代碼如下:

public class Monitor {

public List<Student> getAllStudent(){
List<Student> list = new ArrayList<Student>();
for(int i=0; i<100; i++){
Student student = new Student();
//為每個學生分配個ID
student.setId("學生Id:"+i);
list.add(student);
}
return list;
}

public void printMember() {
List<Student> list = this.getAllStudent();
for (Student student : list) {
System.out.println(student.getId());
}
}
}

public class Principal extends Monitor {

public List<Teacher> getAllTeacher(){
List<Teacher> list = new ArrayList<Teacher>();
for(int i=0; i<30; i++){
Teacher emp = new Teacher();
//為全校老師按順序分配一個ID
emp.setId("老師編號"+i);
list.add(emp);
}
return list;
}

public void printMember() {
super.printMember();

for (Teacher teacher : this.getAllTeacher()) {
System.out.println(teacher.getId());
}
}
}

5. 里氏替換原則

里氏替換原則:

如果對每一個類型為S?的對象o1?,都有類型為T?的對象o2?,使得以T?定義的所有程序P?在所有的對象o1?都代換成o2?時,程序P?的行為沒有發生變化,那么類型S?是類型T的子類型。

一句話來描述就是:只要有父類出現的地方,都可以用子類來替代,而且不會出現任何錯誤和異常。 更通俗點講,就是子類可以擴展父類的功能,但是不能改變父類原有的功能。

其實,對里氏替換原則的定義可以總結如下:

  • 子類可以實現父類的抽象方法,但不能覆蓋父類的非抽象方法
  • 子類中可以增加自己特有的方法
  • 當子類的方法重載父類的方法時,方法的前置條件(即方法的輸入參數)要比父類的方法更寬松
  • 當子類的方法實現父類的方法時(重寫/重載或實現抽象方法),方法的后置條件(即方法的的輸出/返回值)要比父類的方法更嚴格或相等

我們來看個例子:


public class Cache {

public void set(String key, String value) {

}
}

public class RedisCache extends Cache {

public void set(String key, String value) {

}

}

這里例子是沒有違反里氏替換原則的,任何父類、父接口出現的地方子類都可以出現。如果給RedisCache加上參數校驗,如下:


public class Cache {

public void set(String key, String value) {

}
}

public class RedisCache extends Cache {

public void set(String key, String value) {
if (key == null || key.length() < 10 || key.length() > 100) {
System.out.println("key的長度不符合要求");
throw new IllegalArgumentException();
}
}
}

這就違反了里氏替換原則了,因為子類方法增加了加了參數校驗,拋出了異常,雖然子類仍然可以來替換父類。

6.依賴倒置原則

依賴倒置原則定義:

高層模塊不應該依賴低層模塊,兩者都應該依賴其抽象;抽象不應該依賴細節,細節應該依賴抽象。它的核心思想是:要面向接口編程,而不要面向實現編程。

依賴倒置原則可以降低類間的耦合性、提高系統的穩定性、減少并行開發引起的風險、提高代碼的可讀性和可維護性。要滿足依賴倒置原則,我們需要在項目中滿足這個規則:

  • 每個類盡量提供接口或抽象類,或者兩者都具備。
  • 變量的聲明類型盡量是接口或者是抽象類。
  • 任何類都不應該從具體類派生。
  • 使用繼承時盡量遵循里氏替換原則。

我們來看一段違反依賴倒置原則的代碼,業務需求是:顧客從淘寶購物。代碼如下:

class Customer{
public void shopping(TaoBaoShop shop){
//購物
System.out.println(shop.buy());
}
}

以上代碼是存在問題的,如果未來產品變更需求,改為顧客從京東上購物,就需要把代碼修改為:

class Customer{
public void shopping(JingDongShop shop){
//購物
System.out.println(shop.buy());
}
}

如果產品又變更為從天貓購物呢?那有得修改代碼了,顯然這違反了開閉原則?。顧客類設計時,同具體的購物平臺類綁定了,這違背了依賴倒置原則。可以設計一個shop接口,不同購物平臺(如淘寶、京東)實現于這個接口,即修改顧客類面向該接口編程,就可以解決這個問題了。代碼如下:

class Customer{
public void shopping(Shop shop){
//購物
System.out.println(shop.buy());
}
}

interface Shop{
String buy();
}

Class TaoBaoShop implements Shop{
public String buy(){
return "從淘寶購物";
}
}

Class JingDongShop implements Shop{
public String buy(){
return "從京東購物";
}
}

責任編輯:武曉燕 來源: 撿田螺的小男孩
相關推薦

2013-11-01 09:51:39

2024-05-10 09:28:57

Python面向對象代碼

2012-09-19 16:09:43

2019-06-11 14:20:29

人工智能AI

2017-08-30 19:11:38

Linux命令行tab

2016-11-13 16:46:49

2024-08-19 09:22:48

2021-07-09 10:29:50

云計算云計算環境云應用

2023-05-22 15:53:06

JavaScrip代碼素材

2023-11-15 13:12:16

2024-07-03 10:54:09

2021-11-11 15:13:15

人工智能容器技術

2016-04-18 09:18:28

用戶體驗設計產品

2021-10-27 10:15:25

Python新特性編程語言

2022-01-17 11:25:46

代碼Pythonfor

2022-05-17 15:34:08

視覺效果UI 界面設計

2024-07-25 14:36:10

2019-06-21 13:50:33

數據中心

2022-11-15 16:54:54

2022-05-13 09:55:19

Python內置函數
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲欧美日韩一区二区 | 精品欧美一区二区三区久久久 | 人人干天天干 | 性天堂网 | 中文字幕亚洲欧美日韩在线不卡 | 中文字幕在线观看视频一区 | 色综合色综合 | 人妖一区 | 视频一区二区中文字幕 | 国内久久精品 | 青青草一区二区 | 国产在线一区二区三区 | 自拍偷拍第1页 | 伊人久久麻豆 | 日韩精品一区二 | 国产aⅴ爽av久久久久久久 | 91精品国产777在线观看 | 综合精品在线 | 欧美日韩在线观看一区二区三区 | 亚洲一区二区中文字幕 | 91视频免费视频 | 国产精品呻吟久久av凹凸 | 精品一级 | 亚洲精品视频二区 | www.欧美.com | 国产免费观看视频 | 狠狠干网站 | 欧美日韩精品亚洲 | 亚洲国产一区二区在线 | 精品久久精品 | 国产有码| 91中文字幕在线 | 黄网站涩免费蜜桃网站 | 91免费视频 | 在线观看第一页 | 国产小视频在线 | av在线天堂 | 国产香蕉视频在线播放 | 欧美一区二区三区视频 | 亚洲一二视频 | 久久99深爱久久99精品 |