www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當前位置:首頁 > 公眾號精選 > 架構(gòu)師社區(qū)
[導讀]我們繼續(xù)學習架構(gòu)師技能,今天是本系列的第二篇,希望大家持續(xù)關(guān)注。


寫在前面

我們繼續(xù)學習架構(gòu)師技能,今天是本系列的第二篇,希望大家持續(xù)關(guān)注。

可能你不是科班出生,甚至大學都沒念,沒背景沒關(guān)系。我們只要每天進步一點點,一個月、兩個月、半年、一年......。

規(guī)劃性的學習一年半載后,你會覺得開始的你是多么的無知,如若不信,你可試試看!

只要你肯努力,遲早彎道超車!

坐穩(wěn)了,開始發(fā)車

網(wǎng)上都流行那么個段子:搞死一個程序員,不用動刀或槍,只需要修改幾次需求。

開玩笑,確定嗎?

我也是程序員,也和相關(guān)人員互懟過,不過都只是動口不動手。確實,很多時候,相關(guān)人員提的需求確實有點過分,因為要考慮到工期以及現(xiàn)有資源,有句話叫做“你給我做夠資源和時間,我可以給你造飛機火箭!”。

話又說回來,看這篇文章的相信都是程序員,咱們都是一路人。但,我想說其實很多時候,我們也存在問題。

比如:系統(tǒng)設計的可擴展性、可維護性等,大家是否真的有認真想過,甚至部分人連軟件設計的七大原則都還搞不清楚。

不過,很多人也是想好好設計的,但是無賴,給你工期不夠,否則,你需要加班,甚至加班估計夠嗆,從而也就導致系統(tǒng)逐漸變的非常難以維護,臃腫,同樣的功能有n套代碼,到最后就是推翻了出個新版本(長痛不如短痛)。

但,牛逼的人,只要工期不是很離譜,他們寫代碼永遠是看起來非常舒服、還給你預留了很多擴展口子、封裝了很多公用的工具類、抽象出了很多模型等等。

綜上,個人建議同行朋友,尤其是三年左右的,這時候,知識的廣度,深度都要有所涉及,同時,系統(tǒng)設計或者某個模塊的設計也是體現(xiàn)你的能力的點(領(lǐng)導交給你的某個模塊,其實也可以理解為一個系統(tǒng),所以還是要認真對待,大項目也是有多個模塊組成的)。希望大家一定多體會&&領(lǐng)會軟件設計的七大原則。

牛人們的總結(jié)

Robert C.Martin

一個可維護性(Maintainability)較低的軟件設計,通常由于以下4個原因造成

1、過于僵化(Rigidity):設計難以修改

2、過于脆弱(Fragility):設計易遭到破壞(需要修改的時候,容易牽一發(fā)而動全身,不該受到影響的代碼也被迫的破壞掉)

3、牢固性(Immobility):復用率低(當想使用一些功能時會發(fā)現(xiàn)里面的某些代碼不是他想要的,想把這些代碼去掉時,發(fā)現(xiàn)沒辦法去掉,原因是代碼耦合度太高了)

4、粘度過高(Viscosity):難以做正確事情(維護的過程中想進行修改某些代碼,但是發(fā)現(xiàn)沒有辦法進行修改,原因就是粘度太高)

PeterCoad

一個好的系統(tǒng)設計應該具備如下三個特性:

  • 可擴展性(Extendibility)
  • 靈活性(Flexibility)
  • 可插入性(Pluggability)

面向?qū)ο笤O計原則和設計模式也是對系統(tǒng)進行合理重構(gòu),重構(gòu)是在不改變軟件現(xiàn)有功能的基礎上,通過調(diào)整代碼改善軟件的質(zhì)量、性能,使其程序的設計模式和架構(gòu)更趨合理性,提高軟件的擴展性和維護性。

軟件設計七大原則

  • 開閉原則
  • 依賴倒置原則
  • 單一職責原則
  • 接口隔離原則
  • 迪米特法則
  • 里氏替換原則
  • 合成復用原則

關(guān)于這個七大設計原則,相信大部分人也聽說過,甚至很多朋友都學習過,但是始終沒有掌握,希望通過本文的分享,不敢說你一定能掌握,但是至少掌握部分。

本文主要內(nèi)容就是軟件設計的七大原則,重點在于用代碼來演示。

PS:七種原則并不是孤立存在的,他們相互依賴,相互補充。

開閉原則

開閉原則(Open Closed Principle,OCP)由勃蘭特·梅耶提出,他在 1988 年的著作《面向?qū)ο筌浖?gòu)造》中提出:

軟件實體應當對擴展開放,對修改關(guān)閉

這就是開閉原則的經(jīng)典定義。

在現(xiàn)實生活中,開閉原則也有體現(xiàn)。比如,很多互聯(lián)網(wǎng)公司都實行彈性制作息時間,規(guī)定每天工作8小時。意思就是,對于每天工作8小時這個規(guī)定是關(guān)閉的,但是什么時候來、什么時候走是開放的。早來早走,晚來晚走。

作用

開閉原則是面向?qū)ο蟪绦蛟O計的終極目標,它使軟件實體擁有一定的適應性和靈活性的同時具備穩(wěn)定性和延續(xù)性。具體來說,其作用如下。

  1. 對軟件測試的影響:軟件遵守開閉原則的話,軟件測試時只需要對擴展的代碼進行測試就可以了,因為原有的測試代碼仍然能夠正常運行。
  2. 可以提高代碼的可復用性:粒度越小,被復用的可能性就越大;在面向?qū)ο蟮某绦蛟O計中,根據(jù)原子和抽象編程可以提高代碼的可復用性。
  3. 可以提高軟件的可維護性:遵守開閉原則的軟件,其穩(wěn)定性高和延續(xù)性強,從而易于擴展和維護。

實際案例

報名一個網(wǎng)上課程,課程有價格、id、名稱。

//課程接口類 public interface ICourse { String getCourseName(); Integer getCourseId(); BigDecimal getCoursePrice();
} //整個課程生態(tài)有Java架構(gòu)、大數(shù)據(jù)、人工智能、前端、軟件測試等。 //我們創(chuàng)建一個Java架構(gòu)課程的類JavaCourse。 public class JavaCourse implements ICourse { @Override public String getCourseName() { return "JAVA課程";
    } @Override public Integer getCourseId() { return 1;
    } @Override public BigDecimal getCoursePrice() { return new BigDecimal("599");
    }
} public class OpenCloseDemo { public static void main(String[] args) {
        ICourse course = new JavaCourse();
        System.out.println("課程ID=" + course.getCourseId());
        System.out.println("課程名稱=" + course.getCourseName());
        System.out.println("課程價格=" + course.getCoursePrice());
    }
}

運行OpenCloseDemo的main方法,結(jié)果:

課程ID=1
課程名稱=JAVA課程
課程價格=599

現(xiàn)在要給Java架構(gòu)課程做活動,價格優(yōu)惠,比如雙11、618等節(jié)日搞促銷活動。如果修改JavaCourse中的getPrice()方法,則會存在一定風險,可能影響其他地方的調(diào)用結(jié)果。

如何在不修改原有代碼的前提下,實現(xiàn)價格優(yōu)惠這個功能呢?我們再寫一個處理優(yōu)惠邏輯的類——JavaDiscountCourse類(可以思考一下為什么要叫JavaDiscountCourse,而不叫DiscountCourse)。

于是我們就這么干,增加一個java課程的打折類。

public class JavaDiscountCourse extends JavaCourse { public BigDecimal getDiscountCoursePrice(BigDecimal discount) { return super.getCoursePrice().multiply(discount);
    }
} public class OpenCloseDemo { public static void main(String[] args) {
        JavaCourse course = new JavaDiscountCourse();
        DiscountJavaCourse discountJavaCourse = (DiscountJavaCourse) course;
        System.out.println("課程ID=" + course.getCourseId());
        System.out.println("課程名稱=" + course.getCourseName());
        System.out.println("課程價格=" + course.getCoursePrice());
        BigDecimal discount = new BigDecimal(0.5);
        System.out.println("課程折后價=" + discountJavaCourse.getDiscountCoursePrice()discount;
    }
}

運行結(jié)果:

課程ID=1
課程名稱=JAVA課程
課程價格=599
課程折后價=299.5

這樣的話,我們就沒必要動JavaCourse這個類了,其他地方可能還在使用這個JavaCourse中的價格。

依賴倒置原則

定義

依賴倒置原則(Dependence Inversion Principle,DIP)指設計代碼結(jié)構(gòu)時,高層模塊不應該依賴底層模塊,二者都應該依賴其抽象。抽象不應該依賴細節(jié),細節(jié)應該依賴抽象。通過依賴倒置,可以降低類與類之間的耦合性,提高系統(tǒng)的穩(wěn)定性,提高代碼的可讀性和可維護性,并降低修改程序帶來的風險。

案例

我們來看一個案例,還是以課程為例,首先創(chuàng)建一個類Tian。

public class Tian { public void studyJavaCourse(){
        System.out.println("老田在學java課程");
    } public void studyCCourse(){
        System.out.println("老田在學C課程");
    }
}

然后編寫客戶端測試代碼并調(diào)用。

public static void main(String[] args) {
    Tian tian = new Tian();
    tian.studyJavaCourse();
    tian.studyCCourse();
}

隨著學習興趣的暴漲,老田還想學習AI課程。

這個時候,需要業(yè)務擴展,代碼要從底層到高層(調(diào)用層)一次修改代碼。在Tian類中增加studyAICourse()的方法,在高層也要追加調(diào)用。如此一來,在系統(tǒng)發(fā)布以后,實際上是非常不穩(wěn)定的,在修改代碼的同時會帶來意想不到的風險。因此我們優(yōu)化代碼,首先創(chuàng)建一個課程的抽象接口ICourse。

public interface ICourse { void study();
}

然后寫JavaCourse類。

public class JavaCourse implements ICourse { @Override public void study() {
        System.out.println("老田在學習java架構(gòu)師課程");
    }
}

再實現(xiàn)PythonCourse類。

public class PythonCourse implements ICourse { @Override public void study() {
        System.out.println("老田在學習Python課程");
    }
}

最后修改Tian類。

public class Tian { public void study(ICourse course) {
        course.study();
    }
}

來看客戶端測試代碼。

public static void main(String[] args) {
    Tian tian = new Tian();
    ICourse course = new JavaCourse();
    tian.study(course);
}

這時候再看代碼,老田的興趣無論怎么暴漲,對于新的課程,只需要新建一個類,通過傳參的方式告訴Tian,而不需要修改底層代碼。實際上,這是一種大家非常熟悉的方式,叫作依賴注入。

  • 構(gòu)造器注入方式
  • Setter注入方式。

下面來看構(gòu)造器注入方式。

public class Tian { private ICourse course; /** 構(gòu)造函數(shù)方式注入course **/ public Tian(ICourse course) { this.course = course;
    } public void study() {
        course.study();
    }
}

來看客戶端代碼,將JavaCourse對象作為Tian對象的構(gòu)造參數(shù)注入。

public static void main(String[] args) {
        Tian tian = new Tian(new JavaCourse());
        tian.study();
    }
}

根據(jù)構(gòu)造器注入方式,當調(diào)用時,每次都要創(chuàng)建實例。

但,如果Tian是全局單例,則只能選擇Setter注入方式,繼續(xù)修改Tian類的代碼。

public class Tian { private ICourse course; public void setCourse(ICourse course) { this.course = course;
    } public void study() {
        course.study();
    }
}

來看客戶端代碼,調(diào)用Tian對象的setCourse()方法,將JavaCourse對象作為參數(shù)。

public static void main(String[] args) {
        Tian tian = new Tian();
        tian.setCourse(new JavaCourse());
        tian.study();

        tian.setCourse(new PythonCourse());
        tian.study();
    }
}

注:

以抽象為基準比以細節(jié)為基準搭建起來的架構(gòu)要穩(wěn)定得多,因此大家在拿到需求后,要面向接口編程,按照先頂層再細節(jié)的順序設計代碼結(jié)構(gòu)。

單一職責原則

定義

單一職責原則的定義單一職責原則(Simple Responsibility Principle,SRP)指不要存在一個以上導致類變更的原因。

假設有一個Class負責兩個職責,一旦發(fā)生需求變更,修改其中一個職責的邏輯代碼,有可能會導致另一個職責的功能發(fā)生故障。這樣一來,這個Class就存在兩個導致類變更的原因。

如何解決這個問題呢?我們就要分別用兩個Class來實現(xiàn)兩個職責,進行解耦。后期需求變更維護互不影響。這樣的設計,可以降低類的復雜度,提高類的可讀性,提高系統(tǒng)的可維護性,降低變更引起的風險。

總體來說就是一個Class、Interface、Method只負責一項職責。

案例

我們來看代碼實例,還是用課程舉例,我們的課程有直播課和錄播課。直播課不能快進和快退,錄播課可以任意地反復觀看,功能職責不一樣。首先創(chuàng)建一個Course類。

public class ICourse { public void study(String courseName){ if("直播課".equals(courseName)){
            System.out.println("不能快進哦");
        }else{
            System.out.println("可以自定義播放速度,已經(jīng)來回播放");
        }
    }
}

然后看客戶端代碼,無論是直播課還是錄播課,都調(diào)用study()方法的邏輯。

public class Test1 { public static void main(String[] args) {
        Course course = new Course();
        course.study("直播課");
        course.study("看錄像");
    }
}

從上面代碼來看,Course類承擔了兩種處理邏輯。

假如,現(xiàn)在對課程進行加密,那么直播課和錄播課的加密邏輯是不一樣的,必須修改代碼。

而修改代碼邏輯勢必會相互影響,容易帶來不可控的風險。

我們對職責進行分離解耦,分別創(chuàng)建兩個類LiveCourse和ReplayCourse。

LiveCourse直播課程:

public class LiveCourse { public void study(String courseName){
        System.out.println("現(xiàn)場直播,無法修改播放速度");
    }
}

ReplayCourse重播或錄像課程:

public class ReplayCourse { public void study(String courseName){
        System.out.println("看錄像,可以隨便切換播放速度,以及來回播放");
    }
}

客戶端代碼如下,將直播課的處理邏輯調(diào)用LiveCourse類,錄播課的處理邏輯調(diào)用ReplayCourse類。

public class Test2 { public static void main(String[] args) {
        LiveCourse course = new LiveCourse();
        course.study("直播課");

        ReplayCourse replayCourse=new ReplayCourse();
        replayCourse.study("錄播課");
    }
}

當業(yè)務繼續(xù)發(fā)展時,要對課程做權(quán)限。沒有付費的學員可以獲得課程的基本信息,已經(jīng)付費的學員可以獲得視頻流,即學習權(quán)限。

那么對于控制課程層面,至少有兩個職責。我們可以把展示職責和管理職責分離開,都實現(xiàn)同一個抽象依賴。

設計一個頂層接口,創(chuàng)建ICourse接口。

public interface ICourse { //獲得課程的基本信息 String getCourseName(); //獲取視頻流 byte[] getCourseVioeo(); //學習課程 void studyCourse(); //退款 void refundCourse();
}

可以把這個接口拆成兩個接口,創(chuàng)建一個接口ICourseInfo和ICourseManager。ICourseInfo接口的代碼如下。

ICourseInfo接口的代碼如下:

public interface ICourseInfo { //獲取課程名稱 String getCourseName(); //獲取課程視頻流 byte [] getCourseVideo();
}

ICourseManager接口的代碼如下:

public interface ICourseManager { //學習課程 void studyCourse(); //退款 void refundCourse();
}

下面來看方法層面的單一職責設計。有時候,為了偷懶,通常會把一個方法寫成下面這樣。

public void modifyUserInfo(String userName, String address){
    userName = userName;
    address = address;
}

還可能寫成這樣:

private void modifyUserInfo(String userName, String... fields){
    userNmae = userName;
} private void modifyUserInfo(String userName, String address,boolean flag){ if(flag){ //.... }else{ //.... }
    userName = userName;
    address = address;
}

顯然,上面兩種寫法的modifyUserInfo()方法都承擔了多個職責,既可以修改userName,也可以修改address,甚至更多,明顯不符合單一職責原則。那么我們做如下修改,把這個方法拆成兩個。

private void modifyUserName(String userName){
    userName = userName;
} private void modifyAddress(String address){
   address = address;
}

代碼在修改之后,開發(fā)起來簡單,維護起來也容易。在實際項目中,代碼會存在依賴、組合、聚合關(guān)系,在項目開發(fā)過程中還受到項目的規(guī)模、周期、技術(shù)人員水平、對進度把控的影響,導致很多類都不能滿足單一職責原則。但是,我們在編寫代碼的過程中,盡可能地讓接口和方法保持單一職責,對項目后期的維護是有很大幫助的。

接口隔離原則

定義

接口隔離原則的定義接口隔離原則(Interface Segregation Principle,ISP)指用多個專門的接口,而不使用單一的總接口,客戶端不應該依賴它不需要的接口。

這個原則指導我們在設計接口時,應當注意以下幾點。

(1)一個類對另一個類的依賴應該建立在最小接口上。

(2)建立單一接口,不要建立龐大臃腫的接口。

(3)盡量細化接口,接口中的方法盡量少(不是越少越好,一定要適度)。

接口隔離原則符合“高聚合、低耦合”的設計思想,使得類具有很好的可讀性、可擴展性和可維護性。在設計接口的時候,要多花時間思考,要考慮業(yè)務模型,包括還要對以后可能發(fā)生變更的地方做一些預判。所以,在實際開發(fā)中,我們對抽象、業(yè)務模型的理解是非常重要的。

案例

我們來寫一個動物行為的抽象。

IAnimal接口的代碼如下:

public interface IAnimal{ //吃 void eat(); //飛 void fly(); //游泳 void swim();
}

Bird實現(xiàn)類代碼如如下:

public class Bird implements IAnimal{ //吃 @Override void eat(){ //小鳥吃東西 } //飛 @Override void fly(){ //小鳥在飛 } //游泳 void swim(){ //空著,因為小鳥不游泳 }
}

Dog類實現(xiàn)的代碼如下。

public class Dog implements IAnimal{ //吃 @Override void eat(){ //小狗吃東西 } //飛 @Override void fly(){ //空著,因為小狗不會飛 } //游泳 void swim(){ //小狗在游泳 }
}

由上面代碼可以看出,Bird類的swim()方法可能只能空著,Dog類的fly()方法顯然是不可能的。這時候,我們針對不同動物的行為來設計不同的接口,分別設計IEatAnimal、IFlyAnimal和ISwimAnimal接口。

IEatAnimal接口的代碼如下:

public interface IEatAnimal{ //吃 void eat(); 
}

IFlyAnimal接口的代碼如下:

public interface IFlyAnimal{ //飛 void fly(); 
}

ISwimAnimal接口的代碼如下:

public interface ISwimAnimal{ //游泳 void swim(); 
}

Dog只實現(xiàn)IEatAnimal和ISwimAnimal接口。

public class Dog implements IEatAnimal、ISwimAnimal{ //吃 @Override void eat(){ //小狗吃東西 } //游泳 void swim(){ //小狗在游泳 }
}

迪米特法則

迪米特法則的定義

迪米特法則(Law of Demeter,LoD)又叫作最少知道原則(Least KnowledgePrinciple,LKP),指一個對象應該對其他對象保持最少的了解,盡量降低類與類之間的耦合。迪米特法則主要強調(diào)只和朋友交流,不和陌生人說話。出現(xiàn)在成員變量、方法的輸入和輸出參數(shù)中的類都可以被稱為成員朋友類,而出現(xiàn)在方法體內(nèi)部的類不屬于朋友類。

案例

我們來設計一個權(quán)限系統(tǒng),TeamLeader需要查看目前發(fā)布到線上的課程數(shù)量。這時候,TeamLeader要讓Employee去進行統(tǒng)計,Employee再把統(tǒng)計結(jié)果告訴TeamLeader,來看代碼。

Course類的代碼如下:

public class Course{
   
}

Employee類的代碼如下:

public class Employee{ public void checkNumberOfCourse(ListcourseList){
        System.out.println("目前已經(jīng)發(fā)布的課程數(shù)量是:" + courseList.size());
    }
}

TeamLeader類的代碼如下:

public class TeamLeader{ public void commandCheckNumber(Employee employee){
        ListcourseList = new ArrayList(); for(int i=0;i<20;i++){
            courseList.add(new Course());
        }
        employee.checkNumberOfCourse(courseList);
    }
}

客戶端測試代碼如下,將Employee對象作為參數(shù)傳送給TeamLeader對象

public static void main(String [] args){
    TeamLeader teamLeader = new TeamLeader();
    Employee employee = new Employee();
    teamLeader.commandCheckNumber(employee);
}

寫到這里,其實功能都已經(jīng)實現(xiàn),代碼看上去也沒什么問題。根據(jù)迪米特法則,TeamLeader只想要結(jié)果,不需要跟Course產(chǎn)生直接交流。而Employee統(tǒng)計需要引用Course對象,TeamLeader和Course并不是朋友,從如下圖所示的類圖就可以看出來。

改造

Employee類的代碼如下。

public class Employee{ public void checkNumberOfCourse(ListcourseList){
        ListcourseList = new ArrayList(); for(int i=0;i<20;i++){
            courseList.add(new Course());
        }
        System.out.println("目前已經(jīng)發(fā)布的課程數(shù)量是:" + courseList.size());
    }
}

TeamLeader類的代碼如下。

ublic class TeamLeader{ public void commandCheckNumber(Employee employee){ 
        employee.checkNumberOfCourse(courseList);
    }
}

學習軟件設計原則,千萬不能形成強迫癥。當碰到業(yè)務復雜的場景時,需要隨機應變。

里氏替換原則

定義

里氏替換原則(Liskov Substitution Principle,LSP)指如果對每一個類型為T1的對象O1,都有類型為T2的對象O2,使得以T1定義的所有程序P在所有對象O1都替換成O2時,程序P的行為沒有發(fā)生變化,那么類型T2是類型T1的子類型。

定義看上去比較抽象,我們重新解釋一下,可以理解為一個軟件實體如果適用于一個父類,則一定適用于其子類,所有引用父類的地方必須能透明地使用其子類的對象,子類對象能夠替換父類對象,而程序邏輯不變。也可以理解為,子類可以擴展父類的功能,但不能改變父類原有的功能。根據(jù)這個理解,我們對里氏替換原則的定義總結(jié)如下。

(1)子類可以實現(xiàn)父類的抽象方法,但不能覆蓋父類的非抽象方法。

(2)子類中可以增加自己特有的方法。

(3)當子類的方法重載父類的方法時,方法的前置條件(即方法的輸入?yún)?shù))要比父類的方法更寬松。

(4)當子類的方法實現(xiàn)父類的方法時(重寫/重載或?qū)崿F(xiàn)抽象方法),方法的后置條件(即方法的輸出/返回值)要比父類的方法更嚴格或相等。

案例

在講開閉原則的時候,我們埋下了一個伏筆。我們在獲取折扣價格后重寫覆蓋了父類的getPrice()方法,增加了一個獲取源碼的方法getOriginPrice(),這顯然違背了里氏替換原則。我們修改一下代碼,不應該覆蓋getPrice()方法,增加getDiscountPrice()方法。

public class JavaDiscountCourse extends JavaCourse{ public JavaDiscountCourse(Integer id, String name,Double price){ super(id,name,price);
    } public Double getDiscountPrice(){ return suer.getPrice()*0.5 }
}

使用里氏替換原則有以下優(yōu)點:

  • 約束繼承泛濫,是開閉原則的一種體現(xiàn)。
  • 加強程序的健壯性,同時變更時可以做到非常好的兼容性,提高程序的維護性、可擴展性,降低需求變更時引入的風險。

現(xiàn)在來描述一個經(jīng)典的業(yè)務場景,用正方形、矩形和四邊形的關(guān)系說明里氏替換原則,我們都知道正方形是一個特殊的長方形,那么可以創(chuàng)建一個長方形的父類Rectangle類,代碼如下。

public class Rectangle{ private long height; private long width; //set get方法省略 }

創(chuàng)建正方形Square類繼承長方形,代碼如下:

public class Square extends Rectangle{ private long length; //length的get set方法  @Override public long getHeight(){ return getLength();
    } @Override public void setHeight(long height){
        setLength(height);
    } @Override public long getWidth(){ return getLength();
    } @Override public void setWidth(long width){
        setLength(width);
    }
}

在測試類中,創(chuàng)建resize()方法。根據(jù)邏輯,長方形的寬應該大于等于高,我們讓高一直自增,直到高等于寬變成正方形,代碼如下。

public class Test { public static void resize(Rectangle rectangle) { while (rectangle.getWidth() >= rectangle.getHeight()) {
            rectangle.setHeight(rectangle.getHeight() + 1);
            System.out.println("width=" + rectangle.getWidth() + " height=" + rectangle.getHeight());
        }
        System.out.println("Resize end,width=" + rectangle.getWidth() + " height=" + rectangle.getHeight());
    } public static void main(String[] args) {
        Rectangle rectangle = new Rectangle();
        rectangle.setWidth(20);
        rectangle.setHeight(10);
        resize(rectangle);
    }
}

運行結(jié)果:

width=20 height=11
width=20 height=12
width=20 height=13
width=20 height=14
width=20 height=15
width=20 height=16
width=20 height=17
width=20 height=18
width=20 height=19
width=20 height=20
width=20 height=21
Resize end,width=20 height=21

由運行結(jié)果可知,高比寬還大,這在長方形中是一種非常正常的情況。再來看下面的代碼,把長方形替換成它的子類正方形,修改客戶端測試代碼如下。

public class Test { public static void resize(Rectangle rectangle) { while (rectangle.getWidth() >= rectangle.getHeight()) {
            rectangle.setHeight(rectangle.getHeight() + 1);
            System.out.println("width=" + rectangle.getWidth() + "height=" + rectangle.getHeight());
        }
        System.out.println("Resize end,width=" + rectangle.getWidth() + "height=" + rectangle.getHeight());
    } public static void main(String[] args) {
        Square square = new Square();
        square.setWidth(20);
        square.setHeight(10);
        resize(square);
    }
}

此時,運行出現(xiàn)了死循環(huán),違背了里氏替換原則,在將父類替換為子類后,程序運行結(jié)果沒有達到預期。因此,代碼設計是存在一定風險的。里氏替換原則只存在于父類與子類之間,約束繼承泛濫。

再來創(chuàng)建一個基于長方形與正方形共同的抽象——四邊形QuardRangle接口,代碼如下。

public interface QuardRangle { long getHeight(); long getWidth();
}

修改長方形Rectangle類的代碼如下。

public class Rectangle implements QuardRangle{ private long height; private long width; @Override public long getHeight() { return height;
    } public void setHeight(long height) { this.height = height;
    } @Override public long getWidth() { return width;
    } public void setWidth(long width) { this.width = width;
    }
}

修改正方形Square類的代碼如下。

public class Square implements QuardRangle{ private long length; public long getLength() { return length;
    } public void setLength(long length) { this.length = length;
    } @Override public long getHeight(){ return length;
    } @Override public long getWidth(){ return length;
    }

}

此時,如果把resize()方法的參數(shù)換成四邊形QuardRangle類,方法內(nèi)部就會報錯。因為正方形已經(jīng)沒有了setWidth()和setHeight()方法,所以,為了約束繼承泛濫,resize()方法的參數(shù)只能用長方形Rectangle類。

合成復用原則

定義

合成復用原則(Composite/Aggregate Reuse Principle,CARP)指盡量使用對象組合(has-a)或?qū)ο缶酆希╟ontanis-a)的方式實現(xiàn)代碼復用,而不是用繼承關(guān)系達到代碼復用的目的。

合成復用原則可以使系統(tǒng)更加靈活,降低類與類之間的耦合度,一個類的變化對其他類造成的影響相對較小。繼承,又被稱為白箱復用,相當于把所有實現(xiàn)細節(jié)暴露給子類。

組合/聚合又被稱為黑箱復用,對類以外的對象是無法獲取實現(xiàn)細節(jié)的。我們要根據(jù)具體的業(yè)務場景來做代碼設計,其實也都需要遵循面向?qū)ο缶幊蹋∣bject OrientedProgramming,OOP)模型。

案例

還是以數(shù)據(jù)庫操作為例,首先創(chuàng)建DBConnection類。

public class DBConnection{ public String getConnection(){ return "數(shù)據(jù)庫連接";
    }
}

創(chuàng)建ProductDao類。

public class ProdcutDao{ private DBConnection dbConnection; public void setDBConnection(DBConnection dbConnection){ this.dbConnection=dbConnection;
    } public void addProduct(){
        String conn=dbConnection.getConnection();
        System.out.println("使用" + conn + "連接數(shù)據(jù)庫");
    }
}

這是一種非常典型的合成復用原則應用場景。但是,對于目前的設計來說,DBConnection還不是一種抽象,不便于系統(tǒng)擴展。目前的系統(tǒng)支持MySQL數(shù)據(jù)庫連接,假設業(yè)務發(fā)生變化,數(shù)據(jù)庫操作層要支持Oracle數(shù)據(jù)庫。

當然,我們可以在DBConnection中增加對Oracle數(shù)據(jù)庫支持的方法,但是這違背了開閉原則。其實,可以不必修改Dao的代碼,將DBConnection修改為abstract,代碼如下。

public abstract class DBConnection{ public abstract String getConnection();
}

然后將MySQL的邏輯抽離。

public class MySQLConnection extends DBConnection{ @Override public String getConnection(){ return "MySQL 數(shù)據(jù)庫連接";
    }
}

再創(chuàng)建Oracle支持的邏輯。

public class OracleConnection extends DBConnection{ @Override public String getConnection(){ return "Oracle 數(shù)據(jù)庫連接";
    }
}

總結(jié)

學習設計原則是學習設計模式的基礎。在實際開發(fā)過程中,并不是一定要求所有代碼都遵循設計原則,而是要綜合考慮人力、時間、成本、質(zhì)量,不刻意追求完美,要在適當?shù)膱鼍白裱O計原則。這體現(xiàn)的是一種平衡取舍,可以幫助我們設計出更加優(yōu)雅的代碼結(jié)構(gòu)。





免責聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務中斷的風險,如企業(yè)系統(tǒng)復雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉