개방형 원칙

개방-폐쇄 원칙에 따르면 클래스는 확장을 위해 열려 있어야 하지만 수정을 위해서는 폐쇄되어야 합니다.

Classes must remain open for extension but closed for modification.



즉, 클래스에 더 많은 기능을 추가하려면 실제로 클래스 자체를 변경하지 않고 클래스의 동작을 확장하여 수행해야 합니다. 이것은 처음에는 매우 혼란스러워 보일 수 있지만 코드 예제를 보면 이해가 될 것입니다.

이 원칙의 목적은 클래스가 사용되는 곳에서 버그로 이어질 수 있는 수정을 피하는 것입니다.

예시


Senior 클라이언트와 Pregnant 클라이언트에게 할인을 제공하는 상점의 체크아웃 기능을 개발 중이라고 가정해 봅시다. 처음에 기본 코드는 다음과 같습니다.

class Checkout {
  getTotalToPay(total, clientType) {
    let totalToPay = total;
    let discount = 0;

    if (clientType === 'Senior') {
      discount = total * 0.25;
      totalToPay = totalToPay - discount;
    } else if (clientType === 'Pregnant') {
      discount = total * 0.15;
      totalToPay = totalToPay - discount;
    }
    return totalToPay;
  }
}

const checkout = new Checkout();
console.log(checkout.getTotalToPay(100, 'Senior'));
console.log(checkout.getTotalToPay(100, 'Pregnant'));
console.log(checkout.getTotalToPay(100, 'Regular Client'));


코드를 보면 모든 것이 괜찮다고 생각할 수 있지만 현실은 우리가 개방-폐쇄 원칙을 깨고 있다는 것입니다. 우리가 원하는 만큼 많은 클라이언트 유형을 가질 수 있는 만큼 클래스가 확장을 위해 열려 있지만 수정을 위해 닫히지 않기 때문입니다.
Veteran 유형의 클라이언트를 추가하려고 한다고 가정해 보겠습니다. 이렇게 하려면 다른 코드else if를 코드에 추가하기만 하면 됩니다. 결과 코드는 다음과 같습니다.

class Checkout {
  getTotalToPay(total, clientType) {
    let totalToPay = total;
    let discount = 0;

    if (clientType === 'Senior') {
      discount = total * 0.25;
      totalToPay = totalToPay - discount;
    } else if (clientType === 'Pregnant') {
      discount = total * 0.15;
      totalToPay = totalToPay - discount;
    } else if (clientType === 'Veteran') {
      discount = total * 0.10;
      totalToPay = totalToPay - discount;
    }
    return totalToPay;
  }
}

const checkout = new Checkout();
console.log(checkout.getTotalToPay(100, 'Senior'));
console.log(checkout.getTotalToPay(100, 'Pregnant'));
console.log(checkout.getTotalToPay(100, 'Veteran'));
console.log(checkout.getTotalToPay(100, 'Regular Client'));


이 새로운 유형의 클라이언트를 허용하도록 코드를 수정해야 했기 때문에 원칙의 닫힌 부분을 깨뜨렸습니다.

원칙을 준수하기 위해 모든 유형의 클라이언트를 클래스로 분할하고 추상화로 체크아웃 계산을 처리할 수 있습니다. 코드의 리팩토링은 다음과 같습니다.

class Checkout {
  getTotalToPay(total, clientType) {
    let discount = clientType.getDiscount(total);
    let totalToPay = total - discount;
    return totalToPay;
  }
}

class SeniorClient {
  getDiscount(total) {
    return total * 0.25;
  }
}

class PregnantClient {
  getDiscount(total) {
    return total * 0.15;
  }
}

class VeteranClient {
  getDiscount(total) {
    return total * 0.1;
  }
}

class RegularClient {
  getDiscount(total) {
    return total * 0;
  }
}
const checkout = new Checkout();
console.log(checkout.getTotalToPay(100, new SeniorClient()));
console.log(checkout.getTotalToPay(100, new PregnantClient()));
console.log(checkout.getTotalToPay(100, new VeteranClient()));
console.log(checkout.getTotalToPay(100, new RegularClient()));


각 클라이언트 유형을 다른 클래스로 분할하면 Checkout 클래스를 다시 건드릴 필요가 없습니다. 다른 클라이언트 유형을 추가하려면 개방형 원칙에 따라 새 클래스로 생성하여 getTotalToPay 함수에 전달하기만 하면 됩니다.

좋은 웹페이지 즐겨찾기