SOLID 5原則


1 SRP (Single Responsibility Principle)


一つの階級には責任がある.

SRPを適用する前に

public class Production {

    private String name;
    private int price;

    public Production(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public void updatePrice(int price) {
        this.price = price;
    }
}


public class ProductionUpdateService {

    public void update(Production production, int price) {
        //validate price
        validatePrice(price);

        //update price
        production.updatePrice(price);
    }

    private void validatePrice(int price) {
        if (price < 1000) {
            throw new IllegalArgumentException("최소가격은 1000원 이상입니다.");
        }
    }

}
ProductionUpdateServiceの役割は、製品の内容を変更することです.
したがって、価格の有効性を検証するValidatePrice()はProductionUpdateServiceの責任ですか?
プロペラ
価格の有効性を検証するには、製品が実際の価格を変更する責任を負う情報と見なす必要があります.これに基づいて、検証責任を製品に移行するコードは次のとおりです.

SRP適用後

public class Production {

    private static final int MINIMUM_PRICE = 1000;

    private String name;
    private int price;

    public Production(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public void updatePrice(int price) {
        validatePrice(price);
        this.price = price;
    }

    private void validatePrice(int price) {
        if (price < MINIMUM_PRICE) {
            throw new IllegalArgumentException(String.format("최소가격은 %d원 이상입니다.", MINIMUM_PRICE));
        }
    }
}

public class ProductionUpdateService {

    public void update(Production production, int price) {
        //update price
        production.updatePrice(price);
    }

}

2 OCP (Open-Closed Principle)


ソフトウェア要素は拡張で開き、変更で閉じます.

OCPより前

public class Production {
    private String name;
    private int price;
    // N(일반) ,E(전자티켓) ,L(지역상품)...
    private String option;

    public Production(String name, int price, String option) {
        this.name = name;
        this.price = price;
        this.option = option;
    }

    public int getNameLength() {
        return name.length();
    }

    public String getOption() {
        return option;
    }
}

public class ProductionValidator {
    public void validateProduction(Production production) throws IllegalArgumentException {

        if (production.getOption().equals("N")) {
            if (production.getNameLength() < 3) {
                throw new IllegalArgumentException("일반 상품의 이름은 3글자보다 길어야 합니다.");
            }
        } else if (production.getOption().equals("E")) {
            if (production.getNameLength() < 10) {
                throw new IllegalArgumentException("전자티켓 상품의 이름은 10글자보다 길어야 합니다.");
            }
        } else if (production.getOption().equals("L")) {
            if (production.getNameLength() < 20) {
                throw new IllegalArgumentException("지역 상품의 이름은 20글자보다 길어야 합니다.");
            }
        }

    }
}

OCPの原則を適用した後

public interface Validator {

    boolean support(Production production);

    void validate(Production production) throws IllegalArgumentException;

}

public class DefaultValidator implements Validator {
    @Override
    public boolean support(Production production) {
        return production.getOption().equals("N");
    }

    @Override
    public void validate(Production production) throws IllegalArgumentException {
        if (production.getNameLength() < 3) {
            throw new IllegalArgumentException("일반 상품의 이름은 3글자보다 길어야 합니다.");
        }
    }
}


public class ETicketValidator implements Validator {
    @Override
    public boolean support(Production production) {
        return production.getOption().equals("E");
    }

    @Override
    public void validate(Production production) throws IllegalArgumentException {
        if (production.getNameLength() < 10) {
            throw new IllegalArgumentException("전자티켓 상품의 이름은 10글자보다 길어야 합니다.");
        }
    }
}

public class LocalValidator implements Validator {
    @Override
    public boolean support(Production production) {
        return production.getOption().equals("L");
    }

    @Override
    public void validate(Production production) throws IllegalArgumentException {
        if (production.getNameLength() < 20) {
            throw new IllegalArgumentException("지역 상품의 이름은 20글자보다 길어야 합니다.");
        }
    }
}

public class ProductValidator {

    private final List<Validator> validators = Arrays.asList(new DefaultValidator(), new ETicketValidator(), new LocalValidator());

    public void validate(Production production) {
        Validator productionValidator = new DefaultValidator();

        for (Validator localValidator : validators) {
            if (localValidator.support(production)) {
                productionValidator = localValidator;
                break;
            }
        }

        productionValidator.validate(production);
    }
}

3 LSP (Liskov Substitution Principle)

  • 子クラスは、少なくともその親クラスで可能な動作を実行することができる必要があります.
  • 親タイプのオブジェクトを子タイプのオブジェクトに変換する場合でも、親タイプのプログラムを使用して
  • を正常に実行する必要があります.

    LSPを適用する前に

    class Rectangle {
      protected _width: number = -1;
      protected _height: number = -1;
    
      public get width() {
        return this._width;
      }
      public set width(w: number) {
        this._width = w;
      }
    
      public get height() {
        return this._height;
      }
      public set height(h: number) {
        this._height = h;
      }
    
      public get area() {
        return this._width * this._height;
      }
    }
    
    class Square extends Rectangle {
      public set width(w: number) {
        this._width = w;
        this._height = w;
      }
    
      public set height(h: number) {
        this._width = h;
        this._height = h;
      }
    }
    
    const rec: Rectangle = new Rectangle();
    rec.width = 3;
    rec.height = 4;
    
    console.log(rec.area === 12); // true
    
    const rec2: Rectangle = new Square();
    rec2.width = 3;
    rec2.height = 4;
    
    console.log(rec.area === 12); // false
    親Rectangleでの子Squareのarea()機能が正しくありません.

    LSP適用後

    // Shape 인터페이스는 넓이를 구할 수 있는 것(기하학 관점의 면)이라고 가정한다.
    interface Shape {
      readonly area: number;
    }
    
    class Rectangle implements Shape {
      constructor(public width: number, public height: number) {}
    
      public get area() {
        return this.width * this.height;
      }
    }
    
    class Square implements Shape {
      constructor(public width: number) {}
    
      public get area() {
        return this.width ** 2;
      }
    }
    
    const rec: Shape = new Rectangle(3, 4);
    console.log(rec.area); // 12
    
    const sq: Shape = new Square(4);
    console.log(sq.area);  // 16

    4 ISP (Integration Segregation Principle)


    複数の特定のクライアントインタフェースは、1つの汎用インタフェースよりも優れています.

    ISPを適用する前に

    public interface AllInOneDevice {
        void print();
    
        void copy();
    
        void fax();
    }
    
    public class SmartMachine implements AllInOneDevice {
        @Override
        public void print() {
            System.out.println("print");
        }
    
        @Override
        public void copy() {
            System.out.println("copy");
        }
    
        @Override
        public void fax() {
            System.out.println("fax");
        }
    }
    
    package solid.isp.before;
    
    public class PrinterMachine implements AllInOneDevice {
        @Override
        public void print() {
            System.out.println("print");
        }
    
        @Override
        public void copy() {
            throw new UnsupportedOperationException();
        }
    
        @Override
        public void fax() {
            throw new UnsupportedOperationException();
        }
    }
    印刷は上書きされていますが、他の機能を実装する必要はありませんので、Unsupported OperationExceptionが発生します.
    この場合、プリンタがレプリケーション機能を実装しているかどうか分からないため、インタフェースのみを知っているクライアントで予期せぬエラーが発生する可能性があります.

    ISP適用後

    public interface PrinterDevice {
        void print();
    }
    
    public interface CopyDevice {
        void copy();
    }
    
    public interface FaxDevice {
        void fax();
    }
    
    public class SmartMachine implements PrinterDevice, CopyDevice, FaxDevice {
        @Override
        public void print() {
            System.out.println("print");
        }
    
        @Override
        public void copy() {
            System.out.println("copy");
        }
    
        @Override
        public void fax() {
            System.out.println("fax");
        }
    }
    
    // 구현한 객체
    public class PrinterMachine implements PrinterDevice {
    	@Override
     	public void print() {
        		System.out.println("print");
    	}
    }
    
    		// 클라이언트가 사용할 경우
    @DisplayName("하나의 기능만을 필요로 한다면 하나의 인터페이스만 구현하도록 하자")
    @Test
    void singleInterface() {
        PrinterDevice printer = new SmartMachine();
        printer.print();
    }

    5 DIP (Dependency Inversion Principle)


    抽象に頼るには,具体化に頼るわけにはいかない.

    DIPを適用する前に

    public class ProductionService {
    
        private final LocalValidator localValidator;
    
        public ProductionService(LocalValidator localValidator) {
            this.localValidator = localValidator;
        }
    
        public void validate(Production production) {
            localValidator.validate(production);
        }
    
    }
    
    public class LocalValidator {
    
        public void validate(Production production) {
            //validate
        }
    }

    DIP適用後

    public interface Validator {
        void validate(Production production);
    }
    
    public class LocalValidator implements Validator {
        @Override
        public void validate(Production production) {
            //validate
        }
    }
    
    public class ETicketValidator implements Validator {
        @Override
        public void validate(Production production) {
            //validate
        }
    }
    
    public class ProductionService {
    
        private final Validator validator;
    
        public ProductionService(Validator validator) {
            this.validator = validator;
        }
    
        public void validate(Production production) {
            validator.validate(production);
        }
    }

    ソース


    https://bottom-to-top.tistory.com/27
    https://medium.com/humanscape-tech/solid-%EB%B2%95%EC%B9%99-%E4%B8%AD-lid-fb9b89e383ef