厚積薄發打卡Day113:Debug設計模式:設計原則(一)<開閉原則、依賴倒置、單一職責>
開閉原則
定義
一個軟體實體如類、子產品和函數應該對擴充開放,對修改關閉。其優點:提高軟體系統的可複用性及可維護性
用抽象建構架構,用實作擴充細節,實作 面向抽象程式設計。
中心思想:抽象建構架構,實作擴充細節
場景舉例Coding
線上課程購買例子,課程線上購買:
/**
* 課程接口
*/
public interface ICourse {
// 課程id
Integer getId();
// 課程名字
String getName();
// 課程價格
Double getPrice();
}
/**
* Java課程
*/
public class JavaCourse implements ICourse {
private Integer Id;
private String name;
private Double price;
public JavaCourse(Integer id, String name, Double price) {
Id = id;
this.name = name;
this.price = price;
}
@Override
public Integer getId() {
return this.Id;
}
@Override
public String getName() {
return this.name;
}
@Override
public Double getPrice() {
return this.price;
}
}
測試類:
public class Test {
public static void main(String[] args) {
ICourse course = new JavaCourse(1415, "Java企業級别開發", 299d);
System.out.printf("課程id: %d,課程名稱:%s,課程價格:%f 元\n", course.getId(), course.getName(), course.getPrice());
}
}
課程id: 1415,課程名稱:Java企業級别開發,課程價格:299.000000 元
場景變更:現在遇到活動,線上課程通通八折!那應該怎麼修改呢?
~~修改接口?~~那會影響全部實作類,如果有一個實作類則修改一個實作類,那如果實作類很多是否都需要一個個進行修改呢?
**軟體開發設計重點:接口不應該輕易修改,穩定且可靠,作為契約。**同時,越底層的子產品變化影響越大,越高層的子產品變化影響則越小。
那不在影響原有代碼的基礎上進行擴充,最好的方法就是使用Java的多态特性:繼承。
場景實作:
public class DiscountCourse extends JavaCourse {
public DiscountCourse(Integer id, String name, Double price) {
super(id, name, price);
}
public Double getOriginPrice() {
return super.getPrice();
}
public Double getDiscountPrice() {
// 同時這裡可以對折扣價格方案進行處理
return super.getPrice() * 0.8;
}
}
測試類:
public class Test {
public static void main(String[] args) {
ICourse course = new JavaCourse(1415, "Java企業級别開發", 299d);
System.out.printf("課程id: %d,課程名稱:%s,課程價格:%f 元\n", course.getId(), course.getName(), course.getPrice());
DiscountCourse course2 = new DiscountCourse(1415, "Java企業級别開發", 299d);
System.out.printf("課程id: %d,課程名稱:%s,課程原始價格:%f 元,618活動折扣價為:%f 元\n", course2.getId(), course2.getName(), course2.getOriginPrice(), course2.getDiscountPrice());
}
}
課程id: 1415,課程名稱:Java企業級别開發,課程價格:299.000000 元
課程id: 1415,課程名稱:Java企業級别開發,課程原始價格:299.000000 元,618活動折扣價為:239.200000 元
這樣通過繼承的方式,不改變原有代碼的基礎上,實作了折扣價方案的修改實作。
依賴倒置
定義:
- 高層子產品不應該依賴低層子產品,二者都應該依賴其抽象。
- 抽象不應該依賴細節;細節應該依賴抽象
- 核心思想:針對接口程式設計,面向接口程式設計,不要針對實作程式設計
優點:
- 可以減少類間的耦合性、提高系統穩定性,提高代碼可讀性和可維護性,可降低修改程式所造成的風險
場景Coding:
- 舉例:面向實作類程式設計
public class Wayne { public void studyJavaCourse(){ System.out.println("Wayne在學習Java課程"); } public void studyPythonCourse(){ System.out.println("Wayne在學習Python課程"); } }
public class Test { public static void main(String[] args) { // v1 Wayne wayne = new Wayne(); wayne.studyJavaCourse(); wayne.studyPythonCourse(); } }
Wayne在學習Java課程 Wayne在學習Python課程
- 此時Test是應用層,Wayne是底層子產品,如果這時候wayne要學習更多課程,則在底層子產品進行修改,違背了
,直接引用了而不是通過抽象依賴。高層子產品不應該依賴低層子產品
- 此時Test是應用層,Wayne是底層子產品,如果這時候wayne要學習更多課程,則在底層子產品進行修改,違背了
- 引用抽象
依賴實作:// 抽象依賴 public interface IStudy { void studyCourse(); }
public class GoStudy implements IStudy { @Override public void studyCourse() { System.out.println("Wayne在學習Go語言"); } }
public class JavaStudy implements IStudy{ @Override public void studyCourse() { System.out.println("Wayne在學習Java"); } }
底層:public class PythonStudy implements IStudy { @Override public void studyCourse() { System.out.println("Wayne在學習Python"); } }
應用層:public class Wayne { public void studyCourse(IStudy study) { study.studyCourse(); } }
public class Test { public static void main(String[] args) { //v2 Wayne wayne = new Wayne(); wayne.studyCourse(new PythonStudy()); wayne.studyCourse(new JavaStudy()); wayne.studyCourse(new GoStudy()); } }
Wayne在學習Python Wayne在學習Java Wayne在學習Go語言
- 使用構造器注入(單例模式):有點Spring的味道了
public class Wayne {
// ---單例:
private IStudy iStudy;
public Wayne(IStudy iStudy) {
this.iStudy = iStudy;
}
public void studyCourse() {
iStudy.studyCourse();
}
}
//v3:單例模式:
new Wayne(new JavaStudy()).studyCourse();
new Wayne(new PythonStudy()).studyCourse();
- set方法注入:注入方式無需修改底層子產品,即後續想要學習什麼課程直接擴充IStudy即可。
public class Wayne { // set注入 private IStudy iStudy; public void setiStudy(IStudy iStudy) { this.iStudy = iStudy; } public void studyCourse() { iStudy.studyCourse(); } }
綜上總結:面向接口程式設計//v4:set注入 Wayne wayne = new Wayne(); wayne.setiStudy(new JavaStudy()); wayne.studyCourse(); wayne.setiStudy(new GoStudy()); wayne.studyCourse();
單一職責
定義:
不要存在多于一個導緻類變更的原因,一個類/接口/方法隻負責一項職責
優點:降低類的複雜度、提高類的可讀性,提高系統的可維護性、降低變更引起的風險
場景Coding
- 場景耦合
public class Bird { public void mainMoveMode(String birdName) { //使用邊界判斷 if ("鴕鳥".equals(birdName)) { System.out.println(birdName + "用腳走"); } else { System.out.println(birdName + "用翅膀飛"); } } }
public class Test { public static void main(String[] args) { Bird bird = new Bird(); bird.mainMoveMode("鴕鳥"); bird.mainMoveMode("大雁"); bird.mainMoveMode("鴿子🕊"); } }
如果後面增加飛行動物,則需要不斷修改Bird裡面的代碼,同時功能并不單一,違背單一職責原則。鴕鳥用腳走 大雁用翅膀飛 鴿子🕊用翅膀飛
- 類擴充,實作單一原則
public class FlyBird {
public void mainMoveMode(String birdName){
System.out.println(birdName+"用翅膀飛");
}
}
public class WalkBird {
public void mainMoveMode(String birdName){
System.out.println(birdName+"用腳走");
}
}
public class Test {
public static void main(String[] args) {
WalkBird walkBird = new WalkBird();
walkBird.mainMoveMode("鴕鳥");
FlyBird flyBird = new FlyBird();
flyBird.mainMoveMode("大雁");
}
}
鴕鳥用腳走
大雁用翅膀飛
- 接口擴充,以課程舉例
接口在實作類中進行細分:public interface ICourse { // 課程内容管理 String getCourseName(); byte[] getCourseVideo(); // 課程中心管理 void studyCourse(); void refundCourse(); }
public interface ICourseContent {
String getCourseName();
byte[] getCourseVideo();
}
public interface ICourseManager {
void studyCourse();
void refundCourse();
}
public class CourseImpl implements ICourseManager, ICourseContent {
@Override
public void studyCourse() {
}
@Override
public void refundCourse() {
}
@Override
public String getCourseName() {
return null;
}
@Override
public byte[] getCourseVideo() {
return new byte[0];
}
}
- 方法級别,單一職責,方法一般通過對象實作,酌情參考。
public class Method { private void updateUserInfo(String userName, String address) { userName = "wayne"; address = "beijing"; } private void updateUserInfo(String userName, String... properties) { userName = "wayne"; // address = "beijing"; } private void updateUsername(String userName) { userName = "wayne"; } private void updateUserAddress(String address) { address = "beijing"; } private void updateUserInfo(String userName, String address, boolean bool) { if (bool) { //todo something1 } else { //todo something2 } userName = "wayne"; address = "beijing"; } }
總結:單一職責會新增很多類,若盲目使用則會造成類爆炸要根據實際情況,盡量做到接口和方法的單一職責