본문 바로가기

Develop/소프트웨어 설계

[SOLID] Open/Closed 원칙

반응형

목차

Single Responsibility 원칙

Open/Closed 원칙

Liskov Substitution 원칙

Interface Segregation 원칙

Dependency Inversion 원칙

 

 

SOLID 개방 폐쇄 원칙
Open/Closed 원칙

Open/Closed (개방-폐쇄) 원칙

기능의 확장 가능성은 열려 있는 반면, 기능의 변경 가능성은 닫혀있어야 한다.

반드시 필요한 경우가 아니라면 기존 클래스의 기능을 변경하는 작업은 피해야 한다. 기존 코드가 어디에 어떤 방식으로 사용되고 있는지 모두 파악하기 어렵기 때문에 기존 코드의 변경이 어떠한 결과를 초래할지 정확히 예상할 수 없다. 그러므로 기능의 변경이나 확장이 요구될 때는 기존 클래스를 상속하는 방법으로 기존 코드는 유지하면서 요구사항을 만족시킬 수 있도록 설계해야 한다. 그래야 요구사항이 확장할 때마다 예상치 못한 버그를 해결하기 위해 희생되는 시간을 줄여 생산성을 향상시킬 수 있다.

예제

public class Rectangle
{
    public double Width { get; set; }
    public double Height { get; set; }
}

public class CombinedAreaCalculator
{
    public double Area(object[] shapes)
    {
        double area = 0;
        foreach (var shape in shapes)
        {
            if (shape is Rectangle)
            {
                Rectangle rectangle = (Rectangle)shape;
                area += rectangle.Width * rectangle.Height;
            }
        }
        return area;
    }
}

예제 코드는 Rectangle 객체와 제공된 모양의 모든 크기를 계산하는 CombinedAreaCalculator 객체 코드다.

 

public class Circle
{
    public double Radius { get; set; }
}

만약 Circle 클래스가 추가되면,

public class CombinedAreaCalculator
{
    public double Area(object[] shapes)
    {
        double area = 0;
        foreach (var shape in shapes)
        {
            if (shape is Rectangle)
            {
                Rectangle rectangle = (Rectangle)shape;
                area += rectangle.Width * rectangle.Height;
            }
            if (shape is Circle)
            {
                Circle circle = (Circle)shape;
                area += (circle.Radius * circle.Radius) * Math.PI;
            }
        }

        return area;
    }
}

CombinedAreaCalculator 클래스는 Area 함수 안에 if 문을 사용하여 객체를 구분하는 방식으로 기존 코드를 변경하게 된다.

 

따라서 Open/Closed 원칙을 지키기 위해 Shape 추상 클래스를 활용하여 설계를 변경해보자.

public abstract class Shape
{
    public abstract double Area();
}

Rectangle 클래스와 Circle 클래스를 Shape 추상 클래스 상속받도록 하고 각자 Area 함수를 구현하자.

public class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }
    public override double Area()
    {
        return Width * Height;
    }
}

public class Circle : Shape
{
    public double Radius { get; set; }
    public override double Area()
    {
        return Radius * Radius * Math.PI;
    }
}

public class Triangle : Shape
{
    public double Height { get; set; }
    public double Width { get; set; }
    public override double Area()
    {
        return Height * Width * 0.5;
    }
}

변경된 설계를 활용하여 Area 함수를 아래와 같이 변경하면 새로운 Shape 클래스가 추가되더라도 CombinedAreaCalculator 클래스의 소스코드를 변경할 필요가 없다.

public class CombinedAreaCalculator
{
    public double Area(Shape[] shapes)
    {
        double area = 0;
        foreach (var shape in shapes)
        {
            area += shape.Area();
        }
        return area;
    }
}

원칙 적용 전에 심사숙고할 사항

개발을 하다보면 당연히 예기치 못하게 기존 코드를 변경해야 할 때가 있기 마련이다. 원칙을 지키기 위해 기존 코드를 수정하면 쉽게 해결되는 걸 굳이 어렵게 할 필요는 없다. 다만, 새로운 코드를 작성할 때 원칙을 고려해 설계해두었다면 나중에 생길 수고와 버그를 미리 예방할 수 있다.

반응형