Java の 7 つの設計原則

1. 開閉原理

拡張の場合は開き、変更の場合は閉じます。プログラムを拡張する必要がある場合、元のコードを変更することはできず、ホットスワップ効果を達成する必要があります。つまり、プログラムをスケーラブルにし、保守とアップグレードを容易にすることです。

以下は、入力メソッドのスキンを設定する例です。

// 抽象皮肤接口
public interface Skin {
    // 显示的方法
    void display();
}
// 默认皮肤类
public class DefaultSkin implements Skin {
    @Override
    public void display() {
        System.out.println("默认皮肤");
    }
}
// 下班了皮肤
public class OffWorkSkin implements Skin{
    @Override
    public void display() {
        System.out.println("下班了皮肤");
    }
}
// 输入法类
@Data
public class Input {
    // 设置皮肤
    private Skin skin;
    public void display() {
        skin.display();
    }
}
    // 测试
    public static void main(String[] args){
        // 创建输入法对象
        Input input = new Input();
        // 创建皮肤对象
        Skin defaultSkin = new DefaultSkin();
        // 设置默认皮肤
        input.setSkin(defaultSkin);
        // 显示皮肤
        input.display(); // 默认皮肤
        // 换成下班了皮肤
        Skin offWorkSkin = new OffWorkSkin();
        input.setSkin(offWorkSkin);
        input.display();// 下班了皮肤
    }

2. 単一責任の原則

クラスに関する限り、変化の原因は 1 つだけである必要があります。責任は 1 つだけあるはずです。

率直に言えば、クラスまたはメソッドは 1 つのことを実行する必要があります。

ユーザーを変更する例を次に示します。

public class UpdateUser {
    // 不符合单一职责原则
    public void updateUser(String type, String oldPassword, String newPassword, String oldUserName, String newUserName) {
        if ("修改密码".equals(type)) {
            System.out.println(oldPassword + "修改密码为:" + newPassword);
        } else if ("修改账号".equals(type)) {
            System.out.println(oldUserName + "修改账号" + newUserName);
        }
    }
    // 符合单一职责原则
    public void updatePassword(String oldPassword, String newPassword) {
        System.out.println(oldPassword + "修改密码为:" + newPassword);
    }
    public void updateUserName(String oldUserName, String newUserName) {
        System.out.println(oldUserName + "修改账号" + newUserName);
    }
}

上記は操作のタイプに応じて区別されていることがわかります。異なるタイプは異なるロジックを実行し、アカウントの変更とパスワードの変更の 2 つのことは一緒に結合されます。操作中にクライアントが間違ったタイプを渡した場合、それは起こるだろう。

後者は、アカウント変更とパスワード変更のロジックを分離し、それぞれが相互に干渉することなく独自の役割を果たし、単一責任の原則に沿って機能が明確です。

3. 依存関係逆転の原則

高レベルのモジュールは低レベルのモジュールに依存すべきではなく、両方ともその抽象化に依存する必要があり、抽象化は詳細に依存すべきではなく、詳細は抽象化に依存する必要があります。簡単に言えば、実装ではなく抽象化をプログラムする必要があるため、クライアントと実装モジュール間の結合が軽減されます。

以下は、コンピューターの組み立ての例です (簡単な例として、CPU のみがコンピューター用に予約されています、ふふふ)。

// Intel处理器类
public class IntelCpu {
    public void getCpu() {
        System.out.println("使用Intel处理器");
    }
}
// 电脑
@Data
public class Computer {
    private IntelCpu intelCpu; // CPU
    public void run() {
        intelCpu.getCpu();
    }
}
    // 测试
    public static void main(String[] args){
        // 创建电脑对象
        Computer computer = new Computer();
        // 设置CPU
        computer.setIntelCpu(new IntelCpu());
        computer.run(); // 使用Intel处理器
    }

上記で組み立てたコンピューターは Intel CPU しか使用できないことがわかります。AMD CPU に変更したい場合は動作しません。これは依存関係逆転の原則に違反します。変更されたコードは次のとおりです。

// CPU クラス
のパブリック インターフェイス Cpu { 
    void getCpu(); 
} 
// インテル プロセッサ クラスの
パブリック クラス IntelCpu は Cpu を実装します { 
    @Override 
    public void getCpu() { 
        System.out.println("Use Intel プロセッサ"); 
    } 
} 
// Amd プロセッサ クラス
public class AmdCpuimplements Cpu { 
    @Override 
    public void getCpu() { 
        System.out.println("Use Amdprocessor"); 
    } 
} 
// Computer 
@Data 
public class Computer { 
    private Cpu cpu; 
    public void run() { 
        cpu.getCpu(); 
    } 
} 
    // テスト
    public static void main(String[] args){
        // コンピュータ オブジェクトを作成する
        Computer computer = new Computer(); 
        // CPU を Intel プロセッサーに設定します
        computer.setCpu(new IntelCpu()); 
        computer.run(); // Intel プロセッサーを使用します
        // CPU を Amd プロセッシング コンピューターに設定します。 setCpu 
        (new AmdCpu()); 
        computer.run(); // Amd プロセッサを使用
    }

このとき、ユーザーが CPU を交換する必要がある場合、元のインターフェイス コードを変更することなく、CPU インターフェイスを実現するための実装クラスを作成するだけで済みます。これは、オープンとクローズの原理に準拠しています。

4. インターフェース分離原理

クライアントは、使用しないメソッドに依存することを強制されるべきではなく、あるクラスの別のクラスに対する依存関係は、可能な限り最小のインターフェイスに基づく必要があります。

セキュリティ ドアの例を次に示します。

現在、防火および盗難防止機能を備えたブランド A の安全ドアがあり、防火および盗難防止の機能がインターフェイスに抽出されています。つまり、次のとおりです。

// 安全门接口
public interface Door {
    //防火
    void fireproof();
    //防水
    void waterproof();
}
// 安全门接口
public interface Door {
    //防火
    void fireproof();
    //防水
    void waterproof();
}

では、防水機能のみを備えた B ブランドの安全ドアがあればどうなるでしょうか。したがって、セキュリティドアのインターフェースを直接実装することは明らかに不可能ですが、どのように改善する必要がありますか? 改善点は次のとおりです。

// 防火功能
public interface FireProof {
    void fireProof();
}
// 防水功能
public interface WaterProof {
    void waterProof();
}
// A类门,具有防火防水功能
public class ADoor implements WaterProof, FireProof {
    @Override
    public void fireProof() {
        System.out.println("A品牌安全门防火功能");
    }
    @Override
    public void waterProof() {
        System.out.println("A品牌安全门防水功能");
    }
}// B类安全门
public class BDoor implements WaterProof {
    @Override
    public void waterProof() {
        System.out.println("B品牌安全门防水功能");
    }
}

この改善により、異なるブランドのセキュリティドアが異なる機能を持っている場合、機能があればどの機能のインターフェイスを実現できるか、盗難防止インターフェイス、C タイプのドアは火災を実現できることがわかります。 - 防水性と盗難防止性を備えたインターフェース。これがインターフェース分離原則です。

5. デメテルの法則

デメテルの法則は最小知識の原理とも呼ばれます。2 つのソフトウェア エンティティが直接通信する必要がない場合、直接の相互呼び出しは発生せず、呼び出しはサードパーティ経由で転送できます。その目的は、クラス間の結合の度合いを減らし、モジュールの相対的な独立性を向上させることです。

現在のオブジェクト自体、現在のオブジェクトのメンバー オブジェクト、現在のオブジェクトによって作成されたオブジェクト、現在のオブジェクトのメソッド パラメーターなど。これらのオブジェクトは、現在のオブジェクトとこれらのオブジェクトのメソッドに関連付けられ、集約され、または結合されます。直接アクセスできます。

以下はスター、ファン、代理店の例です。

// 明星类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Star {
    private String name;
}
// 粉丝类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Fans {
    private String name;
}
// 经纪公司类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Company {
    private String name;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Agent {
    private Star star;
    private Fans fans;
    private Company company;
    //和粉丝见面的方法
    public void meeting(){
        System.out.println(star.getName() + "和粉丝"+fans.getName() + "见面");
    }
    //和媒体公司洽谈的方法
    public void business(){
        System.out.println(star.getName() + "和" + company.getName() + "洽谈");
    }
}
    // 测试
    public static void main(String[] args) {
        //创建明星对象
        Star star = new Star("严晓波");
        //创建粉丝对象
        Fans fans = new Fans("彭晓锋");
        //创建公司对象
        Company company = new Company("杨永信电疗娱乐有限公司");
        //创建经纪人对象
        Agent agent = new Agent(star, fans, company);
        agent.meeting();// 严晓波和粉丝彭晓锋见面
        agent.business();// 严晓波和杨永信电疗娱乐有限公司洽谈
    }

ファンとの面会やメディア会社との交渉など、スターの日常業務はエージェントが担当しており、ここでのスターの友人はエージェントだが、ファンと会社は他人であるため、ディミットの法則が適している。この例は、ディミッターの法則が主に有名人、ファン、企業の間の結合を減らすことであることを示しています。

6. リスコフ置換原理

基本クラスが出現できる場合は常に、サブクラスが出現する必要があります。サブクラスは親クラスの機能を拡張できますが、親クラスの本来の性能を変えることはできません。つまり、サブクラスが親クラスから継承する場合、新しい関数を完成させるために新しいメソッドを追加する場合を除き、親クラスのメソッドをオーバーライドしないようにしてください。

長方形ではない正方形の例を次に示します。

// 长方形
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Rectangle {
    private Double length;
    private Double width;
}
// 正方形类(错误继承长方形)
public class Square extends Rectangle {
    @Override
    public void setLength(Double length) {
        super.setLength(length);
        super.setWidth(length);
    }
    public void setWidth(Double width) {
        super.setLength(width);
        super.setWidth(width);
    }
}

// 测试类
public class Test {
    public static void main(String[] args){
        // 创建长方形对象
        Rectangle rectangle = new Rectangle(15.0, 10.0);
        // 扩宽
        resize(rectangle);
        // 15.0
        // 16.0
        print(rectangle);
        System.out.println("====================");
        Square square = new Square();
        square.setLength(10.0);
        // 扩宽
        resize(square);
        // 死循环 直到oom
        print(square);
    }
    // 扩宽修正方法
    public static void resize(Rectangle rectangle) {
        while (rectangle.getWidth() <= rectangle.getLength()) {
            rectangle.setWidth(rectangle.getWidth() + 1);
        }
    }
    // 打印长宽
    public static void print(Rectangle rectangle) {
        System.out.println(rectangle.getLength());
        System.out.println(rectangle.getWidth());
    }
}

上記のコードを実行すると、resize() メソッドを呼び出すと通常の長方形は正常に動作することがわかりますが、resize() メソッドを呼び出すオブジェクトが正方形の場合、正方形の長さが等しいため、無限ループが発生します。拡張の条件が満たされていません。したがって、resize() メソッドでは、長方形のパラメータを正方形のパラメータで置き換えることはできず、置き換えると期待される効果が得られないため、正方形と正方形のパラメータの継承関係は維持されません。 Rectangle クラスに違反しています。 Liskov 置換原則によれば、それらの間の継承関係は確立されていません。変更されたコードは次のとおりです。

// 四边形接口,长方形和正方形同属于四边形
public interface Quadrilateral {
    Double getLength(); // 获取长
    Double getWidth(); // 获取宽
}
// 长方形
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Rectangle implements Quadrilateral {
    private Double length;
    private Double width;
}
@Data
public class Square implements Quadrilateral {
    private Double side;
    @Override
    public Double getLength() {
        return side;
    }
    @Override
    public Double getWidth() {
        return side;
    }
}
// 测试类
public class Test {
    public static void main(String[] args){
        // 创建长方形对象
        Rectangle rectangle = new Rectangle(15.0, 10.0);
        // 扩宽
        resize(rectangle);
        // 15.0
        // 16.0
        print(rectangle);
    }
    // 扩宽修正方法
    public static void resize(Rectangle rectangle) {
        while (rectangle.getWidth() <= rectangle.getLength()) {
            rectangle.setWidth(rectangle.getWidth() + 1);
        }
    }
    // 打印长宽
    public static void print(Rectangle rectangle) {
        System.out.println(rectangle.getLength());
        System.out.println(rectangle.getWidth());
    }
}

このように、Square クラスのオブジェクトは、resize() メソッドを使用できません。

7. 合成再利用の原理

複合再利用の原則とは、まず結合や集約などの関連関係を利用して実現し、次に継承関係を利用して実現することを検討することを意味します。

一般に、クラスの再利用は継承再利用と合成再利用の 2 種類に分けられます。

継承の再利用には、シンプルさと実装の容易さという利点がありますが、次のような欠点もあります。

  • 継承の再利用により、クラスのカプセル化が破壊されます。継承により親クラスの実装の詳細がサブクラスに公開され、親クラスがサブクラスに対して透過的になるため、この種の再利用は「ホワイト ボックス」再利用とも呼ばれます。
  • サブクラスは親クラスと高度に結合しています。親クラスの実装を変更すると、サブクラスの実装も変更されるため、クラスの拡張や保守には役立ちません。
  • 再利用の柔軟性が制限されます。親クラスから継承された実装は静的であり、コンパイル時に定義されるため、実行時に変更することはできません。

結合または集約の再利用により、既存のオブジェクトを新しいオブジェクトに組み込んで新しいオブジェクトの一部にすることができます。新しいオブジェクトは既存のオブジェクトの関数を呼び出すことができます。これには次の利点があります。

  • クラスのカプセル化を維持します。メンバー オブジェクトの内部の詳細は新しいオブジェクトには表示されないため、この種の再利用は「ブラック ボックス」再利用とも呼ばれます。
  • オブジェクト間の結合は低いです。抽象化はクラスのメンバー位置で宣言できます。
  • 再利用の自由度が高い。この再利用は実行時に動的に実行でき、新しいオブジェクトはメンバー オブジェクトと同じタイプのオブジェクトを動的に参照できます。

車種の例としては以下のようなものがあります。

継承の再利用
継承の再利用

合成多重化

おすすめ

転載: blog.csdn.net/pyy542718473/article/details/130685633