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