前言
??現在越來越多年輕人不講武德,明明是java面向物件程式員,但是天天寫著面向程序的代碼,你和他們談編程范式、設計原則,設計模式,他們會說不就一個簡單的if/else嗎,用什么設計模式,搞那么復雜!不是閑的,是你的認知水平不夠,設計模式站在軟體設計層面,不僅為了解決眼前業務,更多的是軟體復用性、可讀性、擴展性、可靠性、高內聚、低耦合,先來了解一下常用的設計原則有哪些?
設計原則
- 開閉原則: 即對擴展進行開放,對修改進行關閉,具象點解釋,比如新來一個需求,通過擴展提供方代碼的方式,而不是修改原來呼叫處的代碼,
- 單一職責原則:即一個類只處理一種或一類業務場景需求,高內聚,
- 介面隔離原則:即客戶端不應該依賴它不需要的介面,介面設計時按最新粒度拆分,比如mybatis-plus中的BaseMap違背此原則,
- 依賴倒置原則:即面向抽象類和介面編程,
- 迪米特法則:即最少知道原則,具象點解釋,比如我們只呼叫直接依賴類,不呼叫間接依賴類,不去過多關注依賴類的實作邏輯,做到最少知道,
- 里式替換原則:即父類出現的地方,子類都可以替代,具象點解釋,我們的子類盡量少的重寫父類的方法,或者說重寫時要保證不改變父類邏輯語意,即程式由子類運行時不報邏輯錯誤,
- 合成復用原則:即多用合成和聚合方式,替代繼承關系,降低類之間的耦合度,
下面我們先通過一個簡單案例來解讀設計模式背后常用的設計原則,
案例如下:
業務描述:
??現有一個資訊類APP,資訊分多個頻道,需統計每個頻道(如:籃球頻道、足球頻道、WWE頻道)下,不同資訊型別(如:圖文資訊、圖集資訊、視頻資訊)的點贊數、閱讀數、收藏數,
案例分析:
??設計需滿足后續可以動態擴展資訊頻道和資訊型別且改動盡量少,如再添加一個資訊頻道叫“視頻頻道”,則設計需滿足可以計算“視頻頻道“的圖文、圖集、視頻資訊的點贊/閱讀/收藏數,或者再添加一種資訊型別叫“娛樂資訊”,對應每個資訊頻道也需統計該類資訊的點贊/閱讀/收藏數,
解決方案:
??資訊頻道和資訊型別都有可能變化,那么讓資訊頻道和資訊型別充分解耦,讓其各自可以獨立變化,最后通過聚合的方式使二者產生呼叫關系,此處場景適合橋接模式,讓抽象和具體實作進行分離,各自獨立變化,此案例中,不同的資訊頻道就是我們的抽象,不同的資訊型別就是我們對點贊/閱讀/收藏數的不同具體實作,
類圖分析:

實體代碼:
public interface INewsStatistics {
NewsTypeEnum type();
Integer readCount(Long channelId);
Integer likeCount(Long channelId);
Integer collectCount(Long channelId);
}
先定義資訊統計介面 INewsStatistics
@Service
@RequiredArgsConstructor
public class ImageNewsStatistics implements INewsStatistics {
private final NewsMapper newsMapper;
@Override
public NewsTypeEnum type() {
return NewsTypeEnum.IMAGE;
}
@Override
public Integer readCount(Long channelId) {
return newsMapper.readCount(channelId);
}
@Override
public Integer likeCount(Long channelId) {
return newsMapper.likeCount(channelId);
}
@Override
public Integer collectCount(Long channelId) {
return newsMapper.collectCount(channelId);
}
}
定義圖集資訊統計實作類
@Service
@RequiredArgsConstructor
public class InfoNewsStatistics implements INewsStatistics {
private final NewsMapper newsMapper;
@Override
public NewsTypeEnum type() {
return NewsTypeEnum.INFO;
}
@Override
public Integer readCount(Long channelId) {
return newsMapper.readCount(channelId);
}
@Override
public Integer likeCount(Long channelId) {
return newsMapper.likeCount(channelId);
}
@Override
public Integer collectCount(Long channelId) {
return newsMapper.collectCount(channelId);
}
}
定義圖文資訊統計實作類
@Service
@RequiredArgsConstructor
public class VideoNewsStatistics implements INewsStatistics{
private final NewsMapper newsMapper;
@Override
public NewsTypeEnum type() {
return NewsTypeEnum.VIDEO;
}
@Override
public Integer readCount(Long channelId) {
return newsMapper.readCount(channelId);
}
@Override
public Integer likeCount(Long channelId) {
return newsMapper.likeCount(channelId);
}
@Override
public Integer collectCount(Long channelId) {
return newsMapper.collectCount(channelId);
}
}
定義視頻資訊統計實作類
public abstract class Channel {
private List<INewsStatistics> iNewsStatistics;
public Channel(List<INewsStatistics> iNewsStatistics) {
this.iNewsStatistics = iNewsStatistics;
}
protected Map<NewsTypeEnum, Integer> readCount(Long channelId) {
return iNewsStatistics.stream()
.collect(Collectors.toMap(statistics -> statistics.type(), statistics -> statistics.readCount(channelId)));
}
protected Map<NewsTypeEnum, Integer> likeCount(Long channelId) {
return iNewsStatistics.stream()
.collect(Collectors.toMap(statistics -> statistics.type(), statistics -> statistics.likeCount(channelId)));
}
protected Map<NewsTypeEnum, Integer> collectCount(Long channelId) {
return iNewsStatistics.stream()
.collect(Collectors.toMap(statistics -> statistics.type(), statistics -> statistics.collectCount(channelId)));
}
}
定義頻道Channel 抽象類,并聚合iNewsStatistics資訊統計介面
@Service
public class BasketballChannel extends Channel {
private List<INewsStatistics> iNewsStatistics;
public BasketballChannel(List<INewsStatistics> iNewsStatistics) {
super(iNewsStatistics);
}
@Override
public Map<NewsTypeEnum, Integer> readCount(Long channelId) {
return super.readCount(channelId);
}
@Override
public Map<NewsTypeEnum, Integer> likeCount(Long channelId) {
return super.likeCount(channelId);
}
@Override
public Map<NewsTypeEnum, Integer> collectCount(Long channelId) {
return super.collectCount(channelId);
}
}
定義頻道Channel 抽象類的具體實作類BasketballChannel 籃球頻道,對應的其他FootballChannel足球頻道、WWEChannel頻道類似,此處省略,使用時只需前端傳入對應頻道ID,呼叫對應頻道的readCount、likeCount、collectCount方法,即可查詢該頻道下不同資訊型別的閱讀/點贊/收藏數,
設計原則解讀:
- 開閉原則:資訊頻道和資訊型別的新增,只需添加對應Channel抽象類的子類和INewsStatistics介面的實作類,而呼叫方代碼無需改動,滿足開閉原則,
- 依賴倒置:代碼撰寫都是面向抽象類Channel和INewsStatistics介面,未面向具體實作,符合依賴倒置原則,
- 里式替換:雖然資訊頻道類重寫了父類Channel的方法,但未改變父類方法的邏輯語意,即父類出現的地方,可以讓子類完全替換,所以也符合里式替換原則,
- 單一職責:將不同的資訊頻道和資訊型別拆開,每一個資訊頻道類和資訊型別類只負責各自領域的業務,符合單一職責原則,
- 迪米特法則:Channel類直接依賴INewsStatistics資訊統計類,在Channel類計算點贊/閱讀/收藏數時,直接呼叫INewsStatistics資訊統計類的方法,而沒有過度關注計算方法的細節,也未呼叫其他INewsStatistics類中的間接物件,做到最少知道原則,所以也符合迪米特法則,
- 合成復用原則:此處除了常說的6大設計原則之外,此案例中采用Channel類聚合INewsStatistics介面方式,替代相應的繼承方式,降低類之間的耦合度,也符合合成復用原則,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/246102.html
標籤:其他
上一篇:ffmpeg 解碼音頻(aac、mp3)輸出pcm檔案
下一篇:Java基礎語法(七)
