거의 모든 곳에서 제품 배송에 대해 이야기할 때 가장 먼저 떠오르는 단계는 디자인입니다. 디자인에 더 집중할수록 제품이 더 좋아 보일 것입니다. 모든 디자인에는 제품을 디자인할 때 따라야 하는 몇 가지 디자인 원칙이 있습니다. 따라서 디자인 원칙은 모든 제품 제공에서 중요한 역할을 합니다. 디자인 원칙은 팀의 의사 결정에 도움이 됩니다. 이번 글에서는 'SOLID Principles : Open Closed Principle'에 대해 알아보겠습니다.
몇 가지 간단한 원칙이나 가치 있는 질문은 우리 팀이 관련 결정을 내리도록 안내할 수 있습니다. SOLID 원칙은 소프트웨어를 설계하는 데 사용되는 5가지 원칙의 집합입니다. 사실 'SOLID'라는 단어는 각 원칙의 첫 글자를 포함하는 5가지 원칙의 집합의 약어입니다. SOLID Principles : Open Closed Principle과 관련 개념에 대해 자세히 살펴보겠습니다.
개방-폐쇄 원칙(OCP)이란 무엇입니까?
원칙은 "소프트웨어 구성 요소는 확장을 위해 열려야 하지만 수정을 위해 닫혀 있어야 합니다."라고 말합니다. 간단히 말해서, 클래스, 모듈 및 함수와 같은 소프트웨어 구성 요소는 확장을 위해 열려 있어야 하지만 수정을 위해 닫혀 있어야 합니다.
소프트웨어 구성 요소를 클래스로 간주하는 경우 클래스는 확장을 위해 열려 있어야 하지만 수정을 위해 닫혀 있어야 합니다. 그렇게 함으로써 우리는 기존 코드를 수정하지 못하게 됩니다. 따라서 우리는 또한 행복한 응용 프로그램에서 잠재적인 새로운 버그를 일으키는 것을 중지합니다.
물론 규칙의 한 가지 예외는 기존 코드의 버그를 수정할 때입니다. 따라서 버그 수정 시에만 클래스를 수정해야 합니다.
"확장 가능"은 새로운 요구 사항이 생성될 때 새로운 기능을 추가할 수 있도록 클래스를 설계해야 함을 의미합니다. "수정 폐쇄"는 클래스를 개발한 후에는 버그 수정을 제외하고는 절대 수정해서는 안 된다는 의미입니다.
원칙의 이 두 부분은 모순되는 것처럼 보입니다. 그러나 클래스와 해당 종속성을 올바르게 구성하면 기존 소스 코드를 편집하지 않고도 기능을 추가할 수 있습니다.
디자인과 코드는 기존 코드의 변경을 최소화하거나 전혀 변경하지 않고 새로운 기능을 추가하는 방식으로 이루어져야 합니다. 기능을 확장해야 할 때 - 긴밀한 결합을 피하고 if-else/switch-case 논리를 사용하지 말고 필요에 따라 코드 리팩토링을 수행하십시오.
클래스를 확장하는 방법은 무엇입니까?
클래스를 확장하는 방법은 다음과 같습니다.
- 클래스에서 상속
- 클래스에서 필요한 동작 덮어쓰기
- 클래스의 특정 동작 확장
일반적으로 구체적인 클래스를 사용하는 대신 인터페이스 또는 추상 클래스와 같은 종속성에 대한 추상화를 참조하여 이를 달성합니다. 인터페이스를 구현하는 새 클래스를 만들어 기능을 추가할 수 있습니다. 이는 기존 코드에 새로운 버그를 도입할 위험을 줄여 더 강력한 소프트웨어로 이어집니다.
예: OCP를 위반하는 코드
다양한 모양의 면적을 계산하는 프로그램을 작성해야 한다고 가정해 봅시다. 첫 번째 모양에 대한 클래스를 만드는 것으로 시작합니다. 길이와 너비의 2가지 속성이 있는 Rectangle이라고 가정해 보겠습니다.
public class Rectangle {
public Double length;
public Double width;
}
또한 이 Rectangle의 면적을 계산하는 클래스를 생성합니다. 이 클래스에는 Rectangle을 입력 매개변수로 사용하여 면적을 계산하는 computeRectangleArea() 메서드가 있습니다.
public class AreaCalculator {
public Double calculateRectangleArea(Rectangle rectangle) {
return rectangle.length * rectangle.width;
}
}
여태까지는 그런대로 잘됐다. 이제 두 번째 모양 원에 대한 프로그램을 작성해야 한다고 가정해 보겠습니다. 따라서 단일 속성 반경을 가진 새 클래스 Circle을 즉시 생성합니다.
public class Circle {
public Double radius;
}
그런 다음 새로운 메서드인 computeCircleArea()를 통해 원 계산을 추가하도록 AreaCalculator 클래스를 수정합니다.
public class AreaCalculator{
public Double calculateRectangleArea(Rectangle rectangle){
return rectangle.length * rectangle.width;
}
public Double calculateCircleArea(Circle circle){
return (22 / 7) * circle.radius * circle.radius;
}
}
그러나 위의 솔루션을 설계하는 방식에 결함이 있음을 유의하십시오.
새로운 모양의 오각형이 있다고 가정해 보겠습니다. 이 경우 다시 AreaCalculator 클래스를 수정하게 됩니다. 모양의 유형이 커짐에 따라 AreaCalculator가 계속 변경되고 이 클래스의 소비자가 AreaCalculator가 포함된 라이브러리를 계속 업데이트해야 하므로 이것은 더 복잡해집니다. 결과적으로 AreaCalculator 클래스는 새로운 모양이 올 때마다 수정되기 때문에 확실하게 기준선이 지정(완료)되지 않습니다. 따라서 이 디자인은 수정을 위해 닫히지 않습니다.
AreaCalculator는 최신 방법에 계산 논리를 계속 추가해야 합니다. 우리는 모양의 범위를 실제로 확장하지 않습니다. 오히려 우리는 단순히 추가되는 모든 모양에 대해 조금씩(bit-by-bit) 솔루션을 수행하고 있습니다.
예: OCP를 따르는 코드
이제 Open/Closed 원리를 고수하여 위 디자인의 결점을 해결한 좀 더 우아한 디자인을 봅시다. 우선 디자인을 확장할 수 있도록 하겠습니다. 이를 위해 먼저 기본 유형 Shape를 정의하고 Circle & Rectangle이 Shape 인터페이스를 구현하도록 해야 합니다. 예를 들어, 아래 코드는 개념을 보여줍니다.
public interface Shape {
public Double calculateArea();
}
public class Rectangle implements Shape {
Double length;
Double width;
public double calculateArea() {
return length * width;
}
}
public class Circle implements Shape {
public Double radius;
public Double calculateArea() {
return (22 / 7) * radius * radius;
}
}
앞서 언급했듯이 기본 인터페이스 Shape가 있습니다. 이제 모든 모양이 기본 인터페이스인 모양을 구현합니다. Shape 인터페이스에는 추상 메소드인 computeArea()가 있습니다. 원과 직사각형은 모두 고유한 속성을 사용하여 computeArea() 메서드를 재정의한 자체 구현을 제공합니다. 미래에 삼각형, 정사각형 등과 같은 다른 모양의 면적을 계산하려면 클래스를 변경하지 않고 Shape 인터페이스를 구현할 수 있습니다.
셰이프가 이제 셰이프 인터페이스의 인스턴스이기 때문에 확장성을 어느 정도 가져왔습니다. 이를 통해 개별 클래스 대신 Shape를 사용할 수 있습니다.
마지막 요점은 이러한 모양의 소비자입니다. 소비자는 이제 다음과 같은 AreaCalculator 클래스가 됩니다.
public class AreaCalculator {
public Double calculateShapeArea(Shape shape) {
return shape.calculateArea();
}
}
이 AreaCalculator 클래스는 이제 위에서 언급한 설계 결함을 완전히 제거하고 개방형 원칙을 준수하는 깨끗한 솔루션을 제공합니다.
개방형 폐쇄 원칙의 이점은 무엇입니까?
다음은 코드에 개방형 폐쇄 원칙을 적용할 때의 이점입니다.
더 쉬운 확장성
말할 필요도 없이 개방형 원칙은 코드에 더 나은 확장성을 제공합니다. 수정하지 않고 확장을 허용하는 방식으로 코드를 작성할 때 자체적으로 더 나은 확장성을 얻을 수 있습니다.
유지보수 용이
원칙은 인터페이스 사용을 제안합니다. 코드의 인터페이스는 추가 수준의 추상화를 제공하여 느슨한 결합을 가능하게 합니다. 인터페이스의 구현은 서로 독립적이며 코드를 공유할 필요가 없습니다. 따라서 클라이언트의 계속 변화하는 요구 사항에 따라 코드를 쉽게 유지 관리할 수 있습니다.
유연성
코드를 작성할 때 개방형 폐쇄 원칙을 따를 때 더 유연하게 코드를 확장할 수 있습니다. 또한 향후 변경 요청이 발생하면 코드를 보다 유연하게 확장할 수 있습니다.
개방 폐쇄 원칙의 한계는 무엇입니까?
코드에 대한 변경 사항을 확장으로 통합할 때마다 동일한 구현 전후에 단위 테스트를 수행해야 합니다.
개방형 폐쇄 원칙을 따르는 디자인 패턴은 무엇입니까?
코드를 변경하지 않고 확장하는 데 도움이 되는 다양한 디자인 패턴이 있습니다. 예를 들어 Decorator 패턴은 Open Close 원칙을 따르도록 제안합니다. 또한 Factory Method, Strategy 패턴 및 Observer 패턴을 사용하여 기존 코드에서 최소한의 변경으로 애플리케이션을 설계할 수 있습니다.
그것이 바로 'SOLID Principles : The Open Closed Principle'에 관한 것입니다. 또한 Wikipedia는 이 원칙을 다음과 같이 정의 합니다 . 가장 일반적으로 사용되는 디자인 원칙을 배우려면 ' OOPs 디자인 원칙 '에 대한 기사를 방문하십시오. 'SOLID Principles : Single Responsibility Principle' 외에 다른 원칙은 각각 별도의 기사로 논의됩니다.