a000_Systemarchitektur_Designprinzipien

Video:

https://www.bilibili.com/video/BV1vE411E7ZQ/

Artikel:

https://www.cnblogs.com/pony1223/p/7594803.html

https://www.cnblogs.com/wyq178/p/8284107.html

Die fünf Prinzipien sagen uns lediglich, worauf wir achten müssen, wenn wir ein Framework mit Abstraktion erstellen und Details durch Implementierung erweitern.

Das Prinzip der Einzelverantwortung besagt, dass eine Implementierungsklasse eine Einzelverantwortung haben muss;

Das Liskov-Substitutionsprinzip besagt, dass wir das Vererbungssystem nicht zerstören dürfen;

Das Prinzip der Abhängigkeitsumkehr besagt, dass wir in Richtung Schnittstellen programmieren sollen;

Das Prinzip der Schnittstellenisolation fordert uns auf, beim Entwurf von Schnittstellen einfach zu sein.

Das Demeter-Gesetz besagt, dass wir die Kopplung reduzieren sollen;

Das Open-Close-Prinzip ist die allgemeine Gliederung. Es sagt uns, dass wir offen für Erweiterungen und verschlossen für Änderungen sein sollen.

Das Design der Klasse ist Verhalten und das andere sind Attribute< a i=4 > können Benutzer Verhaltensoperationen abstrahieren, die auf einheitliche Attribute abzielen, und sie von bestimmten Verhaltensausführern ausführen lassen.

1. Prinzip der Einzelverantwortung

1.1. Definition

Es darf nicht mehr als einen Grund für einen Klassenwechsel geben. Laienhaft ausgedrückt ist eine Klasse nur für eine Verantwortung verantwortlich

1.2. Ursprung des Problems

Klasse T ist für zwei verschiedene Verantwortlichkeiten verantwortlich: Verantwortung P1 und Verantwortung P2. Wenn Klasse T aufgrund von Änderungen in den Anforderungen der Verantwortung P1 geändert werden muss, kann dies zu einer Fehlfunktion der Funktion der Verantwortung P2 führen, die ursprünglich normal ausgeführt wurde.

薛定谔的猫理论,用户需求不确定理论

1.3. Lösung

​ Befolgen Sie das Prinzip der Einzelverantwortung. Erstellen Sie zwei Klassen T1 bzw. T2, damit T1 die Verantwortungsfunktion P1 und T2 die Verantwortungsfunktion P2 erfüllen kann. Auf diese Weise besteht bei einer Änderung der Klasse T1 kein Ausfallrisiko für die Verantwortung P2
​In ähnlicher Weise besteht bei einer Änderung von T2 kein Ausfallrisiko für die Verantwortung P1.

1.4. Hinweis

​ Refaktorieren Sie den Code, unmittelbar bevor die Verantwortlichkeiten außerhalb unserer Kontrolle liegen (P1 kann in P11, P12 und P13 unterteilt werden).

//针对一个类的设计 一个类只负责一项职责

public class A单一职责原则 {
    public static void main(String[] args) {
        Controller c = new Controller();
        c.showView("首页");
    }
}

//下面的类各司其职

//视图控制
class Controller{
    public String showView(String pageName){
        Server server = new Server();
        System.out.println("将获取的 '" + server.showView("页面数据") + "' 返回给" + pageName);
        return pageName;
    }
}

//业务逻辑实现
class Server{
    public String showView(String pageDate){
        System.out.println("获取" + pageDate);
        Dao dao = new Dao();
        return dao.showView(new Entity());
    }
}

//数据库操作
class Dao{
    public String showView(Entity en){
        en.uName = "用户名";
        return en.uName;
    }
}

//实体
class Entity{
    public String uName;
}

2. Richter-Substitutionsprinzip

2.1. Definition

definiert义1

​ Wenn es für jedes Objekt o1 vom Typ T1 ein Objekt o2 vom Typ T2 gibt, so haben alle mit T1 definierten Programme P ein Objekt o2 vom Typ T2

Ändert sich das Verhalten von Programm P nicht, dann ist Typ T2 ein Untertyp von Typ T1

definiert义2

​ Alle Orte, die auf eine Basisklasse verweisen, müssen in der Lage sein, Objekte ihrer Unterklasse transparent zu verwenden, d. h. Unterklassen können die Funktionen der übergeordneten Klasse erweitern, aber nicht die ursprünglichen Funktionen der übergeordneten Klasse ändern.

2.2. Ursprung des Problems

Es gibt eine Funktion P1, die von Klasse A vervollständigt wird. Jetzt muss die Funktion P1 erweitert werden, und die erweiterte Funktion ist P, wobei P aus der ursprünglichen Funktion P1 und der neuen Funktion P2 besteht.

​ Die neue Funktion P wird durch die Unterklasse B der Klasse A vervollständigt. Dann kann die Unterklasse B zu Fehlfunktionen der ursprünglichen Funktion P1 führen, während die neue Funktion P2 abgeschlossen wird.

2.3. Lösung

Befolgen Sie bei der Verwendung der Vererbung das Liskov-Substitutionsprinzip. Wenn Klasse B Klasse A erbt, wird zusätzlich zum Hinzufügen einer neuen Methode die neue Funktion P2 abgeschlossen.

Versuchen Sie, Methoden der übergeordneten Klasse A nicht zu überschreiben und Methoden der übergeordneten Klasse A nicht zu überladen. [Manchmal können wir mit letzten Mitteln die Einhaltung erzwingen]

​ Woran wir uns halten müssen: (Unterklassen können die Funktionen der übergeordneten Klasse erweitern, aber nicht die ursprünglichen Funktionen der übergeordneten Klasse ändern)

​ Unterklassen können abstrakte Methoden der übergeordneten Klasse implementieren, aber nicht abstrakte Methoden der übergeordneten Klasse nicht überschreiben.

Unterklassen können ihre eigenen einzigartigen Methoden hinzufügen

​ Wenn eine Unterklasse eine Methode der übergeordneten Klasse überschreibt oder implementiert, sind die Vorbedingungen der Methode (d. h. die formalen Parameter der Methode) lockerer als die Eingabeparameter der Methode der übergeordneten Klasse.

​ Wenn eine Methode einer Unterklasse eine abstrakte Methode der übergeordneten Klasse implementiert, sind die Nachbedingungen der Methode (d. h. der Rückgabewert der Methode) strenger als die der übergeordneten Klasse.

2.4. Hinweis

​ Vererbung umfasst diese Bedeutung:

Alle implementierten Methoden in der übergeordneten Klasse (im Gegensatz zu abstrakten Methoden) legen tatsächlich eine Reihe von Spezifikationen und Verträgen fest, obwohl dadurch nicht alle Unterklassen gezwungen werden, diese Verträge einzuhalten.

Wenn Unterklassen diese nicht abstrakten Methoden jedoch willkürlich ändern, führt dies zu Schäden am gesamten Vererbungssystem. Das Liskov-Substitutionsprinzip drückt diese Bedeutung aus

public class B里氏替换原则 {
    public static void main(String[] args) {

        数据相减 ct1 = new 数据相减();
        System.out.println(ct1.subtract(7, 12));

        数据相乘 ct2 = new 数据相乘();
        System.out.println(ct2.multiplicationAB(7, 12));

        System.out.println(ct1.addAB(7, 12));//不影响父类的功能
        System.out.println(ct2.addAB(7, 12));//不影响父类的功能

    }
}

//父类 数据具体操作
class 数据相加{
    public int addAB(int a, int b){
        return a + b;
    }
}

//子类 数据具体操作
class 数据相减 extends 数据相加{
    public int subtract(int a, int b){
        return a - b;
    }
}

//子类 数据具体操作
//类B继承类A时,除添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法
class 数据相乘 extends 数据相加{
    //将父类的方法重写了 在这里不能影响父类的功能
    // 如果重写了 对于其他的继承子类就不透明了,大家都以为父类原来是相加  但是在这里变成相乘了
    // 所有引用基类的地方必须能透明地使用其子类的对象

    //public int addAB(int a, int b){
    //    return a * b;
    //}

    public int multiplicationAB(int a, int b){
        return a * b;
    }
}

3. Prinzip der Abhängigkeitsumkehr

3.1. Definition

​High-Level-Module sollten nicht von Low-Level-Modulen abhängen, beide sollten von ihren Abstraktionen abhängen; Abstraktionen sollten nicht von Details abhängen; Details sollten von Abstraktionen abhängen.

Das heißt, es ist erforderlich, die Abstraktion und nicht die Implementierung zu programmieren, wodurch die Kopplung zwischen dem Client und dem Implementierungsmodul verringert wird.

3.2. Ursprung des Problems

​ Klasse A hängt direkt von Klasse B ab. Wenn Sie Klasse A so ändern möchten, dass sie von Klasse C abhängt, müssen Sie den Code von Klasse A ändern. In diesem Szenario ist Klasse A im Allgemeinen ein High-Level-Modul, das für komplexe Geschäftslogik verantwortlich ist;

​ Klasse B und Klasse C sind Low-Level-Module, die für grundlegende atomare Operationen verantwortlich sind. Wenn Klasse A geändert wird, bringt dies unnötige Risiken für das Programm mit sich.

3.3. Lösungen

​ Ändern Sie Klasse A so, dass sie von Schnittstelle I abhängt. Klasse B und Klasse C implementieren jeweils Schnittstelle I. Klasse A ist indirekt über Schnittstelle I mit Klasse B oder Klasse C verbunden, wodurch die Wahrscheinlichkeit einer Änderung von Klasse A erheblich verringert wird.

3.4. Hinweis

​ Low-Level-Module sollten versuchen, abstrakte Klassen oder Schnittstellen oder beides zu haben. [Es kann von anderen verwendet werden]

Der deklarierte Typ einer Variablen sollte möglichst eine abstrakte Klasse oder Schnittstelle sein.

​ Befolgen Sie bei der Verwendung der Vererbung das Liskov-Substitutionsprinzip.

​Der Kern des Abhängigkeitsinversionsprinzips besteht darin, dass wir für Schnittstellen programmieren. Wenn wir schnittstellenorientierte Programmierung verstehen, verstehen wir auch Abhängigkeitsinversion.

​ Das Design der Klasse ist Verhalten und das andere sind Attribute , Benutzer können Verhaltensoperationen für einheitliche Attribute abstrahieren, um Attribute zu verwenden.

​ Benutzer ---- Abstraktes Verhalten (Lesen, ich weiß nicht, was ich konkret lesen soll, konzentriere mich nur auf das Verhalten) ---- [Die Person, die das spezifische Verhalten ausführt, liest ein Buch, liest eine Zeitung]

public class C依赖倒置原则 {
    public static void main(String[] args) {
        new Children().readSomething(new Book());
        new Children().readSomething(new Newspaper());
    }
}

//抽象行为
interface IReader{
    void readContent();
}

//具体行为执行者
class Book implements IReader{

    @Override
    public void readContent() {
        System.out.println("从前有座山");
    }
}
//具体行为执行者
class Newspaper implements IReader{

    @Override
    public void readContent() {
        System.out.println("号外号外");
    }
}

//调用者 依赖于抽象
class Children{
    void readSomething(IReader iReader){
        iReader.readContent();
    }
}

4. Prinzip der Schnittstellenisolation

4.1. Definition

​Der Client sollte sich nicht auf Schnittstellen verlassen, die er nicht benötigt; die Abhängigkeit einer Klasse von einer anderen Klasse sollte auf der kleinsten Schnittstelle basieren.

4.2. Ursprung des Problems

​ Klasse A hängt von Klasse B über Schnittstelle I ab, und Klasse C hängt von Klasse D über Schnittstelle I ab. Wenn Schnittstelle I nicht die Mindestschnittstelle für Klasse A und Klasse B ist, müssen Klasse B und Klasse D Methoden implementieren, die sie nicht benötigen .

4.3. Lösungen

Teilen Sie die aufgeblähte Schnittstelle I in mehrere unabhängige Schnittstellen auf, und Klasse A und Klasse C stellen Abhängigkeiten mit den jeweils benötigten Schnittstellen her. Das heißt, das Prinzip der Schnittstellenisolation wird übernommen

4.4. Hinweis

​ Richten Sie eine einzelne Schnittstelle ein, erstellen Sie keine riesige und aufgeblähte Schnittstelle, versuchen Sie, die Schnittstelle zu verfeinern und so wenig Methoden wie möglich in der Schnittstelle zu haben. Mit anderen Worten: Wir müssen für jede Klasse dedizierte Schnittstellen einrichten, anstatt zu versuchen, eine riesige Schnittstelle für alle Klassen zu erstellen, deren Aufruf darauf angewiesen ist.

Bei der Programmierung ist es flexibler, auf mehrere dedizierte Schnittstellen zu setzen, als auf eine umfassende Schnittstelle. Schnittstellen sind „Verträge“, die während des Entwurfs extern festgelegt werden. Durch die dezentrale Definition mehrerer Schnittstellen können wir die Verbreitung externer Änderungen verhindern und die Flexibilität und Wartbarkeit des Systems verbessern.

 ```
 单一职责原则原注重的是职责;而接口隔离原则注重对接口依赖的隔离
 单一职责原则主要是约束类,其次才是接口和方法,它针对的是程序中的实现和细节 
 而接口隔离原则主要约束接口接口,主要针对抽象,针对程序整体框架的构建
 ```

​ Aufrufer –>Abstraktes Operationsverhalten< – Operator – Konkretes Operationsverhalten – Operationsverhaltensausführungsobjekt

//将针对一个操作者的操作行为定义在一个单一的接口中
//多个不同操作者可以共同相同的操作行为
//具体操作者需要哪个操作行为就去实现哪个操作行为,不需要的不实现

//调用者
public class D接口隔离原则 {

    public static void main(String[] args) {
        B b = new B();
        D d = new D();

        A aa = new A();
        aa.depend1(b);
        aa.depend1(d);
        aa.depend2(b);
        aa.depend3(b);

        C cc = new C();
        cc.depend1(b);
        cc.depend2(d);
        cc.depend3(d);
    }
}

//抽象操作行为1
interface I1 {
    void method1();
}

//抽象操作行为2
interface I2 {
    void method2();
    void method3();
}

//抽象操作行为3
interface I3 {
    void method4();
    void method5();
}

//操作者A
class A{
    public void depend1(I1 i){
        i.method1();
    }
    public void depend2(I2 i){
        i.method2();
    }
    public void depend3(I2 i){
        i.method3();
    }
}
//操作者C
class C{
    public void depend1(I1 i){
        i.method1();
    }
    public void depend2(I3 i){
        i.method4();
    }
    public void depend3(I3 i){
        i.method5();
    }
}

//具体操作行为B
class B implements I1, I2{
    public void method1() {
        System.out.println("类B实现接口I1的方法1");
    }
    public void method2() {
        System.out.println("类B实现接口I2的方法2");
    }
    public void method3() {
        System.out.println("类B实现接口I2的方法3");
    }
}

//具体操作行为D
class D implements I1, I3{
    public void method1() {
        System.out.println("类D实现接口I1的方法1");
    }
    public void method4() {
        System.out.println("类D实现接口I3的方法4");
    }
    public void method5() {
        System.out.println("类D实现接口I3的方法5");
    }
}

5. Demeter-Gesetz (am wenigsten bekanntes Prinzip)

5.1. Definition

​ Ein Objekt sollte das geringste Wissen über andere Objekte behalten (eine Klasse sollte versuchen, sich selbst zu kapseln und mit ihren eigenen Freundklassen umzugehen. Im Allgemeinen haben Freundklassen Mitgliedsvariablen und -parameter, und Nichtfreundklassen haben lokale Parameter.)

​ Habe nur Beziehungen zu direkten Freunden und keine Beziehungen zu Fremden (Klassen, die im Methodenkörper erscheinen, sind keine direkten Freunde,Klassen, die in Mitgliedsvariablen und Eingabe- und Ausgabeparametern von Methoden erscheinen, sind direkte Freunde)

​ Eine Entität sollte so wenig wie möglich mit anderen Entitäten interagieren, um die Funktionsmodule des Systems relativ unabhängig zu machen.

Wenn ein Modul geändert wird, sollte es versuchen, keine Auswirkungen auf andere Module zu haben. Die Erweiterung ist relativ einfach. Es stellt eine Einschränkung der Kommunikation zwischen Software-Entitäten dar. Es erfordert, die Breite und Tiefe der Kommunikation zwischen Software-Entitäten so wenig wie möglich einzuschränken.

5.2. Ursprung des Problems

Je enger die Beziehung zwischen den Klassen ist, desto größer ist der Kopplungsgrad. Wenn sich eine Klasse ändert, ist die Auswirkung auf die andere Klasse umso größer.

5.3. Lösung

​ Versuchen Sie, die Kopplung zwischen Klassen so weit wie möglich zu reduzieren (Die Softwareentwicklung sollte auf geringe Kopplung und hohe Kohäsion achten. Es gibt viele Arten der Kopplung, wie z. B. Abhängigkeit, Assoziation, Kombination, Aggregation usw.< /span>)

5.4. Hinweis

Versuchen Sie, die Mitgliedsvariablen Ihrer eigenen Klasse auf privat zu setzen, um externe Zugriffsrechte zu reduzieren.

​ Die Methoden dieser Klasse sollten so privat wie möglich sein und nur die Schnittstellenmethoden für die Geschäftslogik und nicht die internen Methoden für die technischen Details offenlegen.

​ Wenn die Friend-Klasse indirekte Referenzen hat, sollten die indirekten Referenzen zu diesem Zeitpunkt ausgeblendet und nicht in der Mitte angezeigt werden. Wir können eine Zwischenklasse hinzufügen, um alle erforderlichen Klassen zu verwalten

​ Ein Mensch sollte sich so weit wie möglich vervollkommnen und den Kontakt mit anderen Menschen minimieren. „Wenn du blühst, werden Schmetterlinge kommen.“ Blühen kann als „Zusammenhalt“ verstanden werden. Lassen Sie Ihre Fähigkeiten bündeln und zu Ihrem schönsten Selbst erblühen

public class E迪米特法则 {

    public static void main(String[] args) {
        Form1 f1 = new Form1();
        f1.ss();

        Form2 f2 = new Form2();
        f2.ss();
    }
}

class Form1{
    //成员变量  直接的朋友
    private Control1 c1 = new Control1();
    public void ss(){
        c1.getData();
    }
}

class Form2{
    Control2 c2 = new Control2();
    public void ss(){
        c2.getData2();
        c2.getData1();
    }
}

class Control1{
    //Dao1 对Form1来说使陌生人
    private Dao1 d1 = new Dao1();

    private void ccc(){ //减小不必要的暴漏
        System.out.println("C1");
    }

    void getData(){
        this.ccc();
        d1.showMsg();
    }
}

class Control2{
    private Dao2 d2 = new Dao2();
    void getData2(){
        d2.showMsg();
    }

    //Form2要和Dao1产生关系可以依赖Control2(中介)  他两没有必要直接从陌生人产生关系
    private Dao1 d1 = new Dao1();
    void getData1(){
        d1.showMsg();
    }
}

class Dao1{
    void showMsg(){
        System.out.println("D1");
    }
}

class Dao2{
    void showMsg(){
        System.out.println("D2");
    }
}

6. Prinzip der Synthese und Wiederverwendung

6.1. Definition

​ Versuchen Sie, Objektkombinationen anstelle von Vererbung zu verwenden, um eine Wiederverwendung zu erreichen.

6.2. Ursprung des Problems

Eine effektive Nutzung der Vererbung trägt dazu bei, das Problem zu verstehen und die Komplexität zu verringern, während der Missbrauch der Vererbung die Schwierigkeit und Komplexität des Systemaufbaus und der Systemwartung erhöht, sodass die Wiederverwendung der Vererbung sorgfältig eingesetzt werden muss

6.3. Lösungen

​ Verwenden Sie einige vorhandene Objekte über Assoziationsbeziehungen, um sie zu einem Teil neuer Objekte zu machen. Versuchen Sie, Kombinations-/Aggregationsbeziehungen zu verwenden und weniger Vererbung zu verwenden.

6.4. Hinweis

Das Hauptproblem bei der Wiederverwendung durch Vererbung besteht darin, dass die Wiederverwendung der Vererbung die Kapselung des Systems zerstört, da angenommen wird, dass die Vererbung die Implementierungsdetails der Basisklasse für die Unterklasse offenlegt, da die internen Details der Basisklasse normalerweise für die Unterklasse sichtbar sind.

Daher wird diese Art der Wiederverwendung auch als „White-Box“-Wiederverwendung bezeichnet. Wenn sich die Basisklasse ändert, muss sich auch die Implementierung der Unterklasse ändern. Die von der Basisklasse geerbte Implementierung ist statisch und kann zur Laufzeit nicht verwendet werden. Geändert , NEIN

足够的灵活性,而且继承只能在有限的环境中使用
public class F合成复用原则 {
    public static void main(String[] args) {
        B1 b1 = new B1();
        b1.SetA(new A1());
        b1.callFun1();

        C1 c1 = new C1();
        c1.callFun2();
    }
}

class A1{
    void fun1(){
        System.out.println("fun1");
    }

    void fun2(){
        System.out.println("fun2");
    }
}

//B1需要调用A1的两个方法 但是不要使用 B1:A1 这种继承关系
class B1{

    //聚合
    private A1 a;
    public void SetA(A1 a){
        this.a = a;
    }

    public void callFun1(){
        this.a.fun1();
    }
}

//C1也不继承A1
class C1{
    //合成
    private A1 a1 = new A1();

    public void callFun2(){
        a1.fun2();
    }
}

7. Öffnungs- und Schließprinzip

7.1. Definition

​ Eine Softwareeinheit wie eine Klasse, ein Modul und eine Funktion sollte für Erweiterungen offen und für Änderungen geschlossen sein, d. h. das Verhalten des Programms ändern, ohne den Code zu ändern.

7.2. Ursprung des Problems

​ Während des Lebenszyklus der Software, wenn der ursprüngliche Code der Software aufgrund von Änderungen, Upgrades, Wartung usw. geändert werden muss, können Fehler in den alten Code eingeführt werden
​ Außerdem kann es dazu führen, dass wir die gesamte Funktion umgestalten müssen und den ursprünglichen Code erneut testen müssen

7.3. Lösungen

​ Wenn sich die Software ändern muss, versuchen Sie, die Änderung durch eine Erweiterung des Verhaltens der Softwareeinheit zu erreichen, anstatt den vorhandenen Code zu ändern.

​ a. Beschränken Sie Erweiterungen durch Schnittstellen oder abstrakte Klassen und begrenzen Sie die Grenzen von Erweiterungen. Nicht vorhandene öffentliche Methoden dürfen nicht in Schnittstellen oder abstrakten Klassen angezeigt werden.

​ b. Versuchen Sie, Schnittstellen oder abstrakte Klassen für Parametertypen und Referenzobjekte anstelle von Implementierungsklassen zu verwenden.

​ c. Die Abstraktionsschicht sollte so stabil wie möglich gehalten werden und es dürfen keine Änderungen vorgenommen werden, sobald sie festgelegt sind.

//对修改关闭 对扩展开放
//抽象是稳定的  可靠的 不被轻易改变的
//针对一个由多个类组成的结构或者是一个软件模块或者是一个独立的类

//抽象(接口或者抽象类)是开闭原则的关键 也就是对 可变性进行封装
public class G开闭原则 {
    public static void main(String[] args) {
        形状 xz = new 圆形();
        System.out.println("需要的类型:" + xz.showMsg());

        xz = new 三角形();
        System.out.println("需要的类型:" + xz.showMsg());

        xz = new 正方形();
        System.out.println("需要的类型:" + xz.showMsg());
    }
}

// 需要 多个形状时 不需要修改 形状类 而是 创建新的形状类来继承形状类
abstract class 形状{
    //形状可能变动 将其抽象 只有在程序运行时才能确定具体是那种形状
    abstract String showMsg();
}

class 圆形 extends 形状{
    public String showMsg(){
        return "圆形";
    }
}

//扩展的 三角形   正方形
class 三角形 extends 形状{
    public String showMsg(){
        return "三角形";
    }
}

class 正方形 extends 形状{
    public String showMsg(){
        return "正方形";
    }
}

おすすめ

転載: blog.csdn.net/u010952056/article/details/119870145