柚子快報激活碼778899分享:大話設計模式:七大設計原則
柚子快報激活碼778899分享:大話設計模式:七大設計原則
目錄
一、單一職責原則(?Single Responsibility Principle, SRP)?
二、開放封閉原則(?Open-Closed Principle, OCP)
三、依賴倒置原則(?Dependency Inversion Principle, DIP)
四、里氏替換原則(?Liskov Substitution Principle, LSP)
五、接口隔離原則(?Interface Segregation Principle, ISP)
六、合成復用原則(?Composite Reuse Principle, CRP)
七、迪米特法則(?Demeter Principle, DP)
設計模式的七大原則是軟件設計和開發(fā)中的重要指導原則,?它們幫助開發(fā)者創(chuàng)建可擴展、?可維護和靈活的軟件系統(tǒng)。?這些原則包括:?
單一職責原則(?Single Responsibility Principle, SRP)?:?一個類應該只有一個引起變化的原因。?這意味著每個類應該有一個明確的職責,?并且僅負責完成一項功能,?這樣可以使類更加模塊化和可維護。?開放封閉原則(?Open-Closed Principle, OCP)?:?軟件實體(?如類、?模塊或函數(shù))?應該對擴展開放,?對修改關閉。?這意味著當軟件需要適應新的環(huán)境或需求時,?應該通過添加新的代碼來擴展系統(tǒng)的行為,?而不是修改現(xiàn)有的代碼。?依賴倒置原則(?Dependency Inversion Principle, DIP)?:?高層模塊不應該依賴于低層模塊,?它們都應該依賴于抽象。?抽象不應該依賴于細節(jié),?細節(jié)應該依賴于抽象。?這意味著代碼應該依賴于接口或抽象類,?而不是具體的實現(xiàn)類。?里氏替換原則(?Liskov Substitution Principle, LSP)?:?子類型必須能夠替換其基類型而不會產(chǎn)生任何問題。?這確保了繼承關系的正確使用,?子類應該能夠保持與父類相同的接口和行為,?從而保持系統(tǒng)的穩(wěn)定性和可維護性。?接口隔離原則(?Interface Segregation Principle, ISP)?:?客戶端不應該依賴于它不需要的接口。?這意味著接口應該被細分為更小的、?更具體的接口,?這樣客戶端只需要知道和使用它感興趣的方法。?合成復用原則(?Composite Reuse Principle, CRP)?:?優(yōu)先使用對象組合而不是繼承來達到復用的目的。?這意味著在面向?qū)ο笤O計中,?應該優(yōu)先考慮通過組合或聚合關系來復用已有的設計和實現(xiàn),?而不是通過繼承。?迪米特法則(?Demeter Principle, DP)?:?一個對象應當僅與它的朋友(?friendly objects)?說話。?這有助于減少對象之間的耦合,?提高軟件的可維護性和可讀性。?
遵循這些原則可以幫助開發(fā)人員創(chuàng)建更加靈活、?可維護和可擴展的軟件系統(tǒng)。
一、單一職責原則(?Single Responsibility Principle, SRP)?
?定義:一個類應該只有一個引起它變化的原因。
一個類 / 接口 / 方法只負責一項職責
優(yōu)點:降低類的負責度、提高類的可讀性,提高系統(tǒng)的可維護性、降低變更的風險。
public interface UserService {
void updateUser(User user,int opt);
}
傳入修改類型,用戶名,去修改用戶信息。但這里卻違背了(單一職責原則)
改造方法:
public interface UserService {
void changeName(String name);
void changePwd(String pwd);
}
這種更符合我們單一職責原則。
但是我們開發(fā)的時候可能會有些迷茫,什么時候去劃分,什么時候放到一起,那這個時候,就應該去重新審視代碼,哪些是需要合并到一起,哪些是要應用。
這里的原則,既是最簡單的原則,也是最難的原則,難的時候,可能我們不是特別好區(qū)分。
二、開放封閉原則(?Open-Closed Principle, OCP)
定義:一個軟件實體,例如類、模塊、函數(shù),應該對擴展是開放的,對修改是關閉的。
實現(xiàn):用抽象構(gòu)建框架,用實現(xiàn)擴展細節(jié)。
優(yōu)點:提高軟件系統(tǒng)的可復用性及可維護性。
用例:
書籍實體:
public interface IBook {
/**
* 編號
* @return
*/
Integer getId();
/**
* 名稱
* @return
*/
String getName();
/**
* 價格
* @return
*/
Double getPrice();
}
書籍接口:
public class BookImpl implements IBook {
private Integer id;
private String name;
private Double price;
public BookImpl(Integer id, String name, Double price) {
this.id = id;
this.name = name;
this.price = price;
}
@Override
public Integer getId() {
return id;
}
@Override
public String getName() {
return name;
}
@Override
public Double getPrice() {
return price;
}
}
測試用例:?
public class Test {
public static void main(String[] args) {
DiscountBookImpl book = new DiscountBookImpl(1,"java", 100.0);
System.out.println("Book Id: " + book.getId() + ", Title: " + book.getTitle() + ", Price: " + book.getPrice());
}
}
假設我們來了業(yè)務需求,書籍打折,我們在原來的書籍接口上這樣修改:
public class BookImpl implements IBook {
private Integer id;
private String name;
private Double price;
public BookImpl(Integer id, String name, Double price) {
this.id = id;
this.name = name;
this.price = price;
}
@Override
public Integer getId() {
return id;
}
@Override
public String getName() {
return name;
}
@Override
public Double getPrice() {
return this.price * 0.8;
}
}
這樣本身就違背了我們這條原則,正確的做法應該是:
新建一個打折書籍接口:
public class DiscountBookImpl extends BookImpl{
public DiscountBookImpl(Integer id, String name, Double price, Double discount) {
super(id, name, price);
this.discount = discount;
}
@Override
public Double getPrice() {
return super.getPrice() * 0.8;
}
public Double getOriginalPrice() {
return super.getPrice();
}
}
三、依賴倒置原則(?Dependency Inversion Principle, DIP)
定義:程序要依賴于抽象接口,不要依賴于具體實現(xiàn)。簡單的說就是要求對抽象進行編程,不要對實現(xiàn)進行編程。?
面向過程的開發(fā),上層調(diào)用下層,上層依賴于下層,當下層劇烈變動時上層也要跟著變動,這就會導致模塊的復用性降低而大大提高了開發(fā)的成本。
面向?qū)ο蟮拈_發(fā)很好的解決了這個問題,一般情況下抽象的變化概率很小,讓用戶程序依賴于抽象,實現(xiàn)的細節(jié)也依賴于抽象。即使實現(xiàn)細節(jié)不斷變動,只要抽象不變,客戶程序就不需要變化。
優(yōu)點:可以減少類間的耦合性,提高系統(tǒng)的穩(wěn)定性,提高代碼可讀性和維護性,可降低修改程序所造成的風險。
用例:
學生實體:
public class Student {
public void studyJavaCourse() {
System.out.println("學習Java課程");
}
public void studyPythonCourse() {
System.out.println("學習Python課程");
}
}
學生學習課程:
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.studyJavaCourse();
student.studyPythonCourse();
}
}
降低耦合改造:
新建課程接口:
public interface ICourse {
void study();
}
新建Java課程類:
public class JavaCourse implements ICourse {
@Override
public void study(){
System.out.println("JavaCourse study");
}
}
新建Python類:
public class PythonCourse extends ICourse {
@Override
public void study(){
System.out.println("PythonCourse study");
}
}
改造學生實現(xiàn)類:
public class Student {
public void study(ICourse course){
course.study();
}
}
改造測試類:
public class Test {
public static void main(String[] args) {
JavaCourse javaCourse = new JavaCourse();
Student student = new Student();
Student.study(javaCourse);
}
}
改造之后,依賴于上層、下層通過接口訪問。
改造2【利用Spring構(gòu)造器依賴注入原理】
Student改造:
public class Student {
ICourse course;
public Student(ICourse course){
this.course = course;
}
public void study(){
course.study();
}
}
測試類改造:
public class Test {
public static void main(String[] args) {
JavaCourse javaCourse = new JavaCourse();
Student student = new Student(javaCourse);
Student.study();
}
}
改造3【利用Spring Set注入】
Student改造:
public class Student {
ICourse course;
public void setCourse(ICourse course){
this.course = course;
}
public void study(){
course.study();
}
}
測試類改造:
public class Test {
public static void main(String[] args) {
JavaCourse javaCourse = new JavaCourse();
Student student = new Student();
student.setCourse(javaCourse);
Student.study();
}
}
備注:執(zhí)行的過程是一樣的
四、里氏替換原則(?Liskov Substitution Principle, LSP)
定義:派生類(子類)對象可以在程式中替代其基類(超類)對象。
因為繼承帶來的侵入性,增加了耦合性,也降低了代碼靈活性,父類修改代碼,子類也會受到影響,此時就需要里氏替換原則。
子類必須實現(xiàn)父類的抽象方法,但不得重寫(覆蓋)父類的非抽象(已實現(xiàn))方法?!驹趈ava里面可以重寫,但是不建議】子類中可以增加自己特有的方法。當子類覆蓋或?qū)崿F(xiàn)父類的方法時,方法的前置條件(即方法的形參)要比父類方法的輸入?yún)?shù)更寬松。當子類的方法實現(xiàn)父類的抽象方法時,方法的后置條件(即方法的返回值)要比父類更嚴格。
用例:
父類:
public class Parent {
public void method(int i){
System.out.println("Parent method");
}
}
子類:
public class Child extends Parent {
@Override
public void method(int i){
System.out.println("Child method");
}
}
單元測試:
public class Test {
public static void main(String[] args) {
Parent p = new Parent();
p.method(10);
}
}
執(zhí)行結(jié)果:
當改成子類時:
public class Test {
public static void main(String[] args) {
Parent p = new Child();
p.method(10);
}
}
用里氏替換原則進行調(diào)整。
父類:
public abstract class Parent {
public List method(ArrayList i){
return null;
}
public abstract ArrayList
public abstract void abstractMethod(int i);
}
子類:
public class Child extends Parent {
@Override
public ArrayList
System.out.println("Child method");
return null;
}
@Override
public void abstractMethod(int i) {
}
public void method2(int i){
System.out.println("Child method2");
}
}
優(yōu)點:
提高了代碼的重用性,子類擁有父類的方法和屬性;提高代碼的可擴展性,子類可形似于父類,但異于父類,保留自我的特性;
缺點:侵入性、不夠靈活、高耦合
繼承是侵入的,只要繼承就必須擁有父類的所有方法和屬性,在一定程度上約束了子類,降低了代碼的靈活性;增加了耦合,當父類的常量、變量或者方法被修改了,需要考慮子類的修改,所以一旦父類有了變動,很可能會造成非常糟糕的結(jié)果,要重構(gòu)大量的代碼。?
五、接口隔離原則(?Interface Segregation Principle, ISP)
定義:用多個專門的接口,不使用單一的總接口,客戶端不應該依賴他不需要的接口
一個類對應一個類的依賴應該建立在最小的接口上建立單一接口,不要建立龐大臃腫的接口盡量細化接口,接口中的方法盡量少
優(yōu)點:符合高內(nèi)聚低耦合的設計思想,從而使得類具有很好的可讀性、可擴展性和可維護性
通俗點講,把幾個大的接口分成小接口,把接口細化。
動物接口:
public interface IAnimal {
void pref();
void fly();
void swim();
}
狗實體:
public class Dog implements Animal {
@Override
public void prey(){
}
@Override
public void fly(){
}
@Override
public void swim(){
}
}
但是狗不會飛
鳥實體:
public class Bird implements Animal{
@Override
public void prey(){
}
@Override
public void fly(){
}
@Override
public void swim(){
}
}
鳥也不會游泳
這個時候,我們就應該把接口隔離,把它分開。
public class Bird implements IPreyable, IFlyable{
@Override
public void prey(){
}
@Override
public void fly(){
}
}
設計接口的時候呢,過大過小都不好,只有不斷的思考,才能去實現(xiàn)
六、合成復用原則(?Composite Reuse Principle, CRP)
定義:軟件復用時,要盡量先使用組合或者聚合等關聯(lián)關系來實現(xiàn),其次才考慮使用繼承關系來實現(xiàn)。
問題由來:通常類的復用分為繼承復用和合成復用兩種,繼承復用雖然有簡單和易實現(xiàn)的優(yōu)點,但它也存在以下缺點。
繼承復用破壞了類的封裝性。因為繼承會將父類的實現(xiàn)細節(jié)暴露給子類,父類對子類是透明的,所以這種復用又稱為“白箱”復用。子類與父類的耦合度高。父類的實現(xiàn)的任何改變都會導致子類的實現(xiàn)發(fā)生變化,這不利于類的擴展與維護。它限制了復用的靈活性。從父類繼承而來的實現(xiàn)是靜態(tài)的,在編譯時已經(jīng)定義,所以在運行時不可能發(fā)生變化。
解決方案:合成復用原則,是通過將已有的對象納入新對象中,作為新對象的成員對象來實現(xiàn)的,新對象可以調(diào)用已有對象的功能,從而達到復用的效果。
繼承關系:
引擎類:
public class Engine {
//發(fā)動機功率
String power;
//發(fā)動機排量
String displacement;
}
汽車類:
public class Car extends Engine {
//汽車型號
String type;
//汽車重量
String weight;
}
使用合成類進行改造:
public class Car{
//汽車型號
String type;
//汽車重量
String weight;
Engine engine;
}
使用之前的類作為現(xiàn)在的一個屬性
合成是擁有關系,聚合是整體與部分的關系。
聚合:班級,學生,班級是一個整體,學生是一個部分,一個班級里有很多班級,這種關系叫聚合。而汽車,汽車有很多構(gòu)建,他們有很多東西組成。聚合是實心的對象與實心的組合,而合成是空心與實心的實現(xiàn)。兩者非常像。要根據(jù)具體業(yè)務進行區(qū)分。
七、迪米特法則(?Demeter Principle, DP)
定義:一個對象對其他對象保持最少的了解。又叫最少知道原則
通俗點講:小明、小張都想知道彼此新入職公司對方的工資,但是他們作為職業(yè)打工人心里都默念打工人基本素養(yǎng)(社會上的事情少打聽,以免破壞員工和諧)
強調(diào)只和朋友交流朋友:出現(xiàn)在成員變量、方法的輸入、輸出參數(shù)中的類成為成員朋友類,而出現(xiàn)在方法體內(nèi)部的類不屬于朋友類
優(yōu)點:降低類之間的耦合
1987年美國在一個項目組提出的概念。
用例:
學生類:?
public class Student {
}
班長類:
public class Monitor {
public void count(List
System.out.println("學生數(shù):"+students.size());
}
}
老師類:
public class Teacher {
public void commond(Monitor monitor){
List
for(int i=0;i<10;i++){
students.add(new Student());
}
monitor.count(students);
}
}
測試類:
public class Test {
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.commond(new Monitor());
}
}
改造,老師只跟班長進行交流、不跟學生進行交流:
班長類:
public class Monitor {
public void count(){
List
for(int i=0;i<10;i++){
students.add(new Student());
}
System.out.println("學生數(shù):"+students.size());
}
}
老師類:
public class Teacher {
public void commond(Monitor monitor){
monitor.count();
}
}
單元測試類:
public class Test {
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.commond(new Monitor());
}
}
明顯職責變得更清晰了?
?
柚子快報激活碼778899分享:大話設計模式:七大設計原則
相關鏈接
本文內(nèi)容根據(jù)網(wǎng)絡資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。