Unity 개발일지

[C#] 객체지향 프로그래밍 실습하기 본문

C#

[C#] 객체지향 프로그래밍 실습하기

아머르 2024. 7. 2. 11:51

객체지향에 대해 잘 알고있는지 간단한 문제를 풀어보자.

[실전문제]

1. 위 코드에서 삼각형 Triangle 클래스를 새로 작성한다고 했을 때, AreaCalculator의 코드를 수정하지 않고, AreaCalculator가 삼각형의 넓이를 계산하도록 만들 수 있을까요?

더보기

IShape 라는 인터페이스를 정의하여 각 도형 클래스가 해당 인터페이스를 상속하게 하였고, AreaCalculatorExtensions 확장 메서드를 사용하여 AreaCalculator를 수정하지 않고, 삼각형 넓이를 계산할 수 있도록 CalculateTriangleArea 메서드를 작성하였다.

 

확장 메서드는 특정 클래스의 인스턴스 메서드처럼 호출된다.

확장 메서드는 첫 번째 매개변수로 지정된 클래스의 인스턴스를 메서드 호출 대상 객체로 사용하는데,

this AreaCalculator calculator는 메서드를 호출하는 객체 자체를 의미하므로, Main에서 해당 메서드를 사용할 때는 이를 매개변수로 넘길 필요가 없으므로 기존 클래스의 인스턴스처럼 사용될 수 있다.

// 인터페이스 구현
public interface IShape
{
    double CalculateArea();
}

// 삼각형 넓이
public class Triangle : IShape
{
    public double BaseLength { get; set; }
    public double Height { get; set; }

    public Triangle(double baseLength, double height)
    {
        BaseLength = baseLength;
        Height = height;
    }

    public double CalculateArea()
    {
        return 0.5 * BaseLength * Height;
    }
}

// 원 넓이
public class Circle : IShape
{
    public double Radius { get; set; }

    public Circle(double radius)
    {
        Radius = radius;
    }

    public double CalculateArea()
    {
        return Math.PI * Radius * Radius;
    }
}

// 사각형 넓이
public class Rectangle : IShape
{
    public double Width { get; set; }
    public double Height { get; set; }

    public Rectangle(double width, double height)
    {
        Width = width;
        Height = height;
    }

    public double CalculateArea()
    {
        return Width * Height;
    }
}

// 면적 계산기
public class AreaCalculator
{
    public double CalculateRectangleArea(Rectangle rectangle)
    {
        return rectangle.CalculateArea();
    }

    public double CalculateCircleArea(Circle circle)
    {
        return circle.CalculateArea();
    }
}

// 확장 메서드 작성
public static class AreaCalculatorExtensions
{
    public static double CalculateTriangleArea(this AreaCalculator calculator, Triangle triangle)
    {
        return triangle.CalculateArea();
    }
}

class Program
{
    static void Main(string[] args)
    {
        Rectangle rectangle = new Rectangle { Width = 5, Height = 10 };
        Circle circle = new Circle { Radius = 3 };
        Triangle triangle = new Triangle(3, 4); // 삼각형 추가

        AreaCalculator calculator = new AreaCalculator();

        double rectangleArea = calculator.CalculateRectangleArea(rectangle);
        double circleArea = calculator.CalculateCircleArea(circle);
        double triangleArea = calculator.CalculateTriangleArea(triangle); // 삼각형 넓이 추가

        Console.WriteLine($"Rectangle Area: {rectangleArea}");
        Console.WriteLine($"Circle Area: {circleArea}");
        Console.WriteLine($"Circle Area: {triangleArea}");
    }
}

 

2. 1번 질문 답변을 바탕으로 위 코드가 유지보수 측면이 어려운 부분을 설명해주세요.

더보기

현재 AreaCalculator 클래스는 각 도형마다 별도의 메서드를 가지고 있으며, 새로운 도형을 추가할 때마다 클래스를 수정해야 한다. 이는 SOLID원칙 중 OCP(개방폐쇄 원칙)에 위배된다.

1. 새로운 도형이 추가될 때마다 AreaCalculator 클래스 수정 필요

2. 특정 도형에 강하게 의존하여 코드의 유연성 저하

3. 새로운 도형을 추가할 때마다 코드가 복잡해지고, 유지보수에 어려움이 생긴다.

 

따라서, AreaCalculator 클래스에서 인터페이스를 활용하여 다른 도형들을 계산하도록 확장하는 것이 좋아보인다.

public class AreaCalculator
{
    public double CalculateArea(IShape shape)
    {
        return shape.CalculateArea();
    }
}

 

 

[설명문제]

1. SOLID 원칙에 대해 설명해주세요.

더보기

SOLID원칙은 객체지향 개발에서 유지보수성과 확장성을 높이기 위한 다섯가지 설계 원칙이다.

1. 단일책임 원칙 (SRP, Single Responsibility Principle)

 클래스(객체)는 단 하나의 책임만 가져야 한다.

 

2. 개방폐쇄 원칙 (OCP, Open Closed Principle)

 확장에는 열려있어야 하며, 수정에는 닫혀있어야 한다.

 

3. 리스코프 치환 원칙 (LSP, Liskov Substitution Principle)

 서브 타입(자식)은 언제나 기반(부모) 타입으로 교체할 수 있어야 한다.

 다형성 원리를 이용하기 위한 원칙 개념으로, 상위 클래스 타입으로 객체를 선언하여 하위 클래스의 인스턴스를 받으면, 업캐스팅된 상태에서 부모의 메서드를 사용해도 동작이 의도대로 흘러가야한다.

(부모 메서드의 오버라이딩을 조심스럽게 따져가며 해야한다.)

 

4. 인터페이스 분리 원칙 (ISP, Interface Segregation Principle)

 인터페이스를 각각 사용에 맞게끔 잘 분리해야한다는 설계원칙이다.

 SRP원칙이 클래스의 단일 책임을 강조한다면, ISP는 인터페이스의 단일 책임을 강조한다.

즉, 한번 인터페이스를 분리하여 구성해놓고, 나중에 무언가 수정사항이 생겨서 또 인터페이스들을 분리하는 행위를  하지 말라는 개념이다.

 

5. 의존 역전 원칙 (DIP, Dependency Inversion Principle)

 어떤 Class를 참조해서 사용해야하는 상황이 생긴다면, 그 Class를 직접 참조하는 것이 아니라, 그 대상의 상위 요소(추상클래스, 인터페이스)로 참조하라는 원칙.

즉, 의존 관계를 맺을 때 변화하기 쉬운 것 또는 자주 변화하는 것보다는 변화하기 어려운 것, 변화가 거의 없는 것에 의존하라는 개념이다.

 

[출처] - SOLID원칙에 대한 정리가 잘 되어있어 링크를 남겨두었다.

 

💠 객체 지향 설계의 5가지 원칙 - S.O.L.I.D

객체 지향 설계의 5원칙 S.O.L.I.D 모든 코드에서 LSP를 지키기에는 어려움. 리스코프 치환 원칙에 따르면 자식 클래스의 인스턴스가 부모 클래스의 인스턴스를 대신하더라도 의도에 맞게 작동되어

inpa.tistory.com

 

2. 객체지향 프로그래밍의 속성 중 하나인 다형성과 이를 활용한 설계의 장점에 대해 설명해주세요.

더보기

다형성은 동일한 인터페이스를 통해 서로 다른 클래스의 객체를 동일하게 다룰 수 있게한다.

이를 통해 코드의 재사용성을 높이고, 유지보수성을 향상시키며, 코드의 유연성을 확보할 수 있다.

 

3. override와 overload에 대해 설명해주세요.

더보기

override는 상속받은 메서드를 재정의 하는것이며, overload는 같은 이름의 메서드를 매개변수의 종류나 개수를 달리하여 여러개 정의하는 것이다.

 

4. 확장 메서드에 대해 설명하고 어떻게 활용했는지 알려주세요.

더보기

확장 메서드는 기존 타입에 새로운 메서드를 추가하는 기능으로 static 클래스를 사용하여 정의된다.

확장 메서드를 이용해 기존 클래스를 수정하지 않고도 삼각형의 넓이를 구하는 기능을 추가할 수 있다.

public static class AreaCalculatorExtensions
{
    public static double CalculateTriangleArea(this AreaCalculator calculator, Triangle triangle)
    {
        return triangle.CalculateArea();
    }
}

 위에서 AreaCalculator 클래스의 코드를 바꾸지 않고 삼각형의 넓이를 구하는 코드를 추가하려고 작성하였다.

확장 메서드를 통해 코드의 가독성과 재사용성을 높이고, 클래스의 기본 동작을 수정하지 않고도 새로운 기능을 추가할 수 있다.

 

 

[실습문제]

Array 클래스에 확장 메서드를 이용하여 배열의 평균값을 계산하는 기능을 추가하세요.

이 확장 메서드는 CalculateAverage라는 이름을 가지며,

int[] 타입의 배열을 입력으로 받아 평균값을 double타입으로 반환해야 합니다.

더보기
using System;

namespace ArrayExtensions
{
    public static class ArrayExtension
    {
        public static double CalculateAverage(this int[] array)
        {
            if ( array == null || array.Length == 0 ) return 0;
            double sum = array.Sum();
            return sum / array.Length;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            int[] numbers = { 1, 2, 3, 4, 5 };
            
            // 확장 메서드를 사용하여 배열의 평균값을 계산하고 출력
            double average = numbers.CalculateAverage();
            Console.WriteLine($"The average is: {average}");
        }
    }
}

 

this int[] array : 확장 메서드가 배열 인스턴스에서 호출될 수 있도록 한다.

array.Sum() : 배열의 모든 요소를 합산한다.

sum / array.Length : 합산된 값을 배열의 길이로 나눠 평균을 계산한다.

 

 

 

 

 

 

반응형