728x90
반응형
SMALL

인터페이스 분리 원칙

Interface Segregation Principle, ISP

“클라이언트는 자신이 사용하지 않는 메서드에 의존 관계를 맺으면 안 된다.” - 로버트 C. 마틴

'특정 클라이언트를 위한 인터페이스 여러개가 범용 인터페이스 하나보다 낫다'

클라이언트가 필요하지 않는 기능을 가진 인터페이스에 의존해서는 안되고 최대한 인터페이스를 작게 유지해야 합니다.

인터페이스 분리 원칙을 위반한 계산기

원칙 위반 증상

필요하지 않은 기능을 강제로 구현해야하는 상황이 발생합니다.필요하지 않은 혹은 사용 못 하는 기능이 강제로 구현되어 사용하지 못하도록 예외처리를 해야하는 상황이 발생할 수 있습니다.

public class Calculator {
    public int calculate(AbstractOperation operation, int firstNumber, int secondNumber){
        return operation.operate(firstNumber, secondNumber);
    }
}
public class Client {
    public static void main(String[] args) throws Exception {
        int firNum = 140;
        int secNum = 60;

        // 연산 결과만 출력
        DisplayTypeA displayTypeA = new DisplayTypeA();
        displayTypeA.displayResult(new AddOperation(), firNum, secNum);
        // displayTypeA.displayResultWithOperator(new AddOperation(), firNum, secNum); // Error 발생

        // 연산 과정까지 출력
        DisplayTypeB displayTypeB = new DisplayTypeB();
        displayTypeB.displayResultWithOperator(new AddOperation(), firNum, secNum);
        // displayTypeB.displayResult(new AddOperation(), firNum, secNum); // Error 발생
    }
}
public abstract class AbstractOperation {
    public abstract int operate(int firstNumber, int secondNumber);
    public abstract String getOperator();
}
public interface Display {
    public abstract void displayResult(AbstractOperation operation, int firstNumber, int secondNumber) throws Exception;
    public abstract void displayResultWithOperator(AbstractOperation operation, int firstNumber, int secondNumber) throws Exception;
}
// 연산 결과만 출력
public class DisplayTypeA extends Calculator implements Display {

    @Override
    public void displayResult(AbstractOperation operation, int firstNumber, int secondNumber) {
        int answer = operation.operate(firstNumber, secondNumber);
        System.out.println(answer);
    }

    @Override
    public void displayResultWithOperator(AbstractOperation operation, int firstNumber, int secondNumber) throws Exception {
        throw new Exception("동작 불가");
    }
}
// 연산 과정을 포함한 출력
public class DisplayTypeB extends Calculator implements Display {

    @Override
    public void displayResult(AbstractOperation operation, int firstNumber, int secondNumber) throws Exception {
        throw new Exception("동작 불가");
    }

    @Override
    public void displayResultWithOperator(AbstractOperation operation, int firstNumber, int secondNumber) {
        int answer = operation.operate(firstNumber, secondNumber);
        String operator = operation.getOperator();
        System.out.println(firstNumber + " " + operator + " " + secondNumber + " = " + answer);
    }
}
public class AddOperation extends AbstractOperation {
    @Override
    public int operate(int firstNumber, int secondNumber) {
        return firstNumber + secondNumber;
    }

    @Override
    public String getOperator() {
        return "+";
    }
}
public class SubstractOperation extends AbstractOperation {
    @Override
    public int operate(int firstNumber, int secondNumber) {
        return firstNumber - secondNumber;
    }

    @Override
    public String getOperator() {
        return "-";
    }
}
public class MultiplyOperation extends AbstractOperation {
    @Override
    public int operate(int firstNumber, int secondNumber) {
        return firstNumber * secondNumber;
    }

    @Override
    public String getOperator() {
        return "*";
    }
}
public class DivideOperation extends AbstractOperation {
    @Override
    public int operate(int firstNumber, int secondNumber) {
        return firstNumber / secondNumber;
    }

    @Override
    public String getOperator() {
        return "/";
    }
}

필요하지 않은 기능을 강제로 구현하고 있습니다.

사용하지 못하도록 예외처리를 구현하고 있습니다.

원칙 적용 방법

필요하지 않은 기능을 강제로 구현하지 않도록 인터페이스를 분리합니다.

연산 결과를 보여주는 방법마다 인터페이스를 구현합니다.

DisplayResult : 인터페이스, 연산결과만 출력

displayResult : 추상 메서드

DisplayWithOperator : 인터페이스, 연산과정 포함 출력displayResultWithOperator : 추상 메서드

728x90
반응형
LIST

'개발 > JAVA' 카테고리의 다른 글

생성자와 세터의 차이  (0) 2023.02.10
의존성 역전 원칙  (0) 2023.01.27
리스코프 치환 원칙  (0) 2023.01.27
개방-폐쇄 원칙  (0) 2023.01.27
단일 책임 원칙  (0) 2023.01.27
728x90
반응형
SMALL

리스코프 치환 원칙

Liskov Substitution Principle, LSP

“서브 타입은 언제나 자신의 기반 타입(base type)으로 교체할 수 있어야 한다.” - 로버트 C. 마틴

'하위 클래스의 인스턴스는 상위형 객체 참조변수에 대입해 상위 클래스의 인스턴스 역할을 하는 데 문제가 없어야 한다'

리스코프 치환 원칙 - wiki

리스코프 치환 원칙을 위반한 계산기

원칙 위반 증상

객체의 타입을 확인합니다.(instanceof)자식 클래스명이 연관되거나 의존성이 있는 클래스에서 자주 발생합니다.

public class Calculator {
    // 연산 기능을 추상화된 부모클래스에 의존하여 처리한다.
    public int calculate(AbstractOperation operation, int firstNumber, int secondNumber) {
        // 나누기 연산 - 0처리 추가
        if(operation instanceof DivideOperation){
            if(secondNumber == 0){
                return -99999;
            }
        }
        return operation.operate(firstNumber, secondNumber);
    }
}
public class Client {
    public static void main(String[] args) {

        Calculator calculator = new Calculator();

        int firNum = 140;
        int secNum = 60;

        int answer = calculator.calculate(new AddOperation(), firNum, secNum);
        System.out.println(" + answer = " + answer);

        answer = calculator.calculate(new SubstractOperation(), firNum, secNum);
        System.out.println(" - answer = " + answer);

        answer = calculator.calculate(new MultiplyOperation(), firNum, secNum);
        System.out.println(" * answer = " + answer);

        answer = calculator.calculate(new DivideOperation(), firNum, secNum);
        System.out.println(" / answer = " + answer);
    }
}
public abstract class AbstractOperation {
    public abstract int operate(int firstNumber, int secondNumber);
}
public class AddOperation extends AbstractOperation {
    @Override
    public int operate(int firstNumber, int secondNumber) {
        return firstNumber + secondNumber;
    }
}
public class SubstractOperation extends AbstractOperation {
    @Override
    public int operate(int firstNumber, int secondNumber) {
        return firstNumber - secondNumber;
    }
}
public class MultiplyOperation extends AbstractOperation {
    @Override
    public int operate(int firstNumber, int secondNumber) {
        return firstNumber * secondNumber;
    }
}
public class DivideOperation extends AbstractOperation {
    @Override
    public int operate(int firstNumber, int secondNumber) {
        return firstNumber / secondNumber;
    }
}

추가적인 기능이 필요한 자식 클래스가 존재하기 때문에 타입 확인을 진행하고 있습니다.

따라서 자식 클래스의 인스턴스가 부모클래스의 인스턴스 역할을 수행하는데 문제가 없다고 할 수 없습니다.

원칙 적용 방법

부모 와 자식 클래스 사이의 행위가 일관성이 있도록 추상화를 좀 더 정교하게 구현합니다.

연산 기능을 추상화한 부모 클래스에 피연산자 값의 유효성 검사를 진행하는 메서드를 추가해 줍니다.

계산기 클래스에서는 이 메서드를 사용하여 유효성 검사를 진행하고 이 유효성 검사가 필요한 자식 클래스에서는 이 추가된 유효성 검사 조건을 구체화 합니다.

728x90
반응형
LIST

'개발 > JAVA' 카테고리의 다른 글

의존성 역전 원칙  (0) 2023.01.27
인터페이스 분리 원칙  (0) 2023.01.27
개방-폐쇄 원칙  (0) 2023.01.27
단일 책임 원칙  (0) 2023.01.27
SOLID  (0) 2023.01.27
728x90
반응형
SMALL

개방-폐쇄 원칙

Open-Closed Principle, OCP

“소프트웨어 엔티티(클래스, 모듈, 함수 등)는 확장에 대해서는 열려 있어야 하지만 변경에 대해서는 닫혀 있어야 한다.” - 로버트 C. 마틴

'자신의 확장에는 열려 있고, 주변의 변화에 대해서는 닫혀 있어야 한다'

Java의 Database 연결

Java에는 데이터베이스에 접속할 수 있도록 도와주는 JDBC 라는 API 가 존재합니다.

이 JDBC 를 개방-폐쇄 원칙의 예로 들 수 있습니다.

개발을 할 때 우리는 Oracle, MySQL 등 다양한 데이터베이스를 사용하게 됩니다.

이때 만약 개발이 진행되는 과정에서 데이터베이스를 변경하게 된다고 하더라도 우리가 그동안 개발하던 코드를 전부 수정할 필요는 없습니다.

연결할 때 필요한 ID, PWD, URL 등의 설정을 담당하는 부분만 변경하면 쉽게 데이터베이스를 변경할 수 있습니다.

개방-폐쇄 원칙을 위반한 계산기

원칙 위반 증상

기능 확장을 할 때마다 기존코드를 수정해야 합니다.if else 블록이 자주 등장합니다.기능 확장을 위한 코드 수정을 할 때 여러 클래스에서 다발적으로 진행이 됩니다.

public class Calculator {
    private AddOperation addOperation;
    private SubstractOperation substractOperation;
    private MultiplyOperation multiplyOperation;
    private DivideOperation divideOperation;

    public Calculator(AddOperation addOperation, SubstractOperation substractOperation, MultiplyOperation multiplyOperation, DivideOperation divideOperation) {
        this.addOperation = addOperation;
        this.substractOperation = substractOperation;
        this.multiplyOperation = multiplyOperation;
        this.divideOperation = divideOperation;
    }

    public int add(int firstNumber, int secondNumber){
        return addOperation.operate(firstNumber, secondNumber);
    }

    public int substract(int firstNumber, int secondNumber){
        return substractOperation.operate(firstNumber, secondNumber);
    }

    public int multiply(int firstNumber, int secondNumber){
        return multiplyOperation.operate(firstNumber, secondNumber);
    }

    public int divide(int firstNumber, int secondNumber){
        return divideOperation.operate(firstNumber, secondNumber);
    }
}
public class Client {
    public static void main(String[] args) {
        Calculator calculator = new Calculator(
                new AddOperation(),
                new SubstractOperation(),
                new MultiplyOperation(),
                new DivideOperation()
        );

        int firNum = 140;
        int secNum = 60;

        int answer = calculator.add(firNum, secNum);
        System.out.println(" + answer = " + answer);

        answer = calculator.substract(firNum, secNum);
        System.out.println(" - answer = " + answer);

        answer = calculator.multiply(firNum, secNum);
        System.out.println(" * answer = " + answer);

        answer = calculator.divide(firNum, secNum);
        System.out.println(" / answer = " + answer);
    }
}
public class AddOperation {
    public int operate(int firstNumber, int secondNumber){
        return firstNumber + secondNumber;
    }
}
public class SubstractOperation {
    public int operate(int firstNumber, int secondNumber){
        return firstNumber - secondNumber;
    }
}
public class MultiplyOperation {
    public int operate(int firstNumber, int secondNumber){
        return firstNumber * secondNumber;
    }
}
public class DivideOperation {
    public int operate(int firstNumber, int secondNumber){
        return firstNumber / secondNumber;
    }
}

연산자를 추가하려고 할 때 코드의 변화가 너무 많이 일어납니다.

if else 조건문 블록이 존재하며 연산자 추가 시 코드변화가 빈번하게 일어납니다.

원칙 적용 방법

변화되는 부분을 추상화해서 변화를 고정 시킵니다.

기능이 추가될 때 클래스의 상속을 통해서 하위 클래스에서 기능을 구현 하도록 합니다.

기존코드를 수정하지 않아도 객체 상속의 다형성 원리에 의해 기능이 확장 되도록 합니다.

연산 클래스는 추상화된 부모 클래스를 상속받아 기능별로 구현합니다.

AbstractOperation : 추상 클래스operate : 추상 메서드

728x90
반응형
LIST

'개발 > JAVA' 카테고리의 다른 글

인터페이스 분리 원칙  (0) 2023.01.27
리스코프 치환 원칙  (0) 2023.01.27
단일 책임 원칙  (0) 2023.01.27
SOLID  (0) 2023.01.27
변수의 종류  (0) 2023.01.20

+ Recent posts