내일배움캠프 53일차 TIL - 객체지향 프로그래밍

권태하·2024년 7월 2일
0
post-thumbnail

확인 문제

using System;

// 사각형 클래스
public class Rectangle
{
    public double Width { get; set; }
    public double Height { get; set; }

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

// 원 클래스
public class Circle
{
    public double Radius { get; set; }

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

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

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

class Program
{
    static void Main(string[] args)
    {
        Rectangle rectangle = new Rectangle { Width = 5, Height = 10 };
        Circle circle = new Circle { Radius = 3 };

        AreaCalculator calculator = new AreaCalculator();

        double rectangleArea = calculator.CalculateRectangleArea(rectangle);
        double circleArea = calculator.CalculateCircleArea(circle);

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

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

내 답:
새로 만들어질 Triangle에 접근할 방법이 현재로서 없기 때문에 불가능합니다.

모범 답:
불가능(가능은하지만 권장할 방법은 아님)

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

내 답:
위와 같은 코드는 새로운 클래스가 추가되거나 기존 클래스의 이름 등 요소가 변경 될 경우 그에 맞춰 새로운 코드를 추가하거나 변경해야 하기 때문에 유지보수 측면에서 어려움이 생깁니다.

모범 답:
예시 코드 중 CalculateArea 내부의 메서드들이 각 단일 클래스들을 매개변수로 받고있기 때문에 Triangle 클래스를 새로 만들고 면적 계산시 CalculateArea쪽에 메서드를 추가해야하는 필요성이 생기게 된다.
삼각형 면적 계산 방법중 유사한 방법이 사각형이기 때문에 사각형의 CalculateArea 메서드를 virtual로 바꾸고, Triangle : Rectangular 식으로 상속하게되면 가능은하지만 가독성이 떨어지기에 권장할 방법은 아니다.

만약 이 코드 자체를 리팩토링하게된다면 다음과 같이 작성할 수 있다.

interface IShape
{
  double CalculateArea();
}
public class Rectangle : IShape
{
  double width,height;
  public double CalculateArea()
    return width*height;
}
public class Circle : IShape
{
  double radius;
  public double CalculateArea()
    return Math.PI * radius * radius;
}
public class Triangle : IShape
{
  double width,height;
  public double CalculateArea()
    return 0.5*width*height;
}
public class Calculator
{
  public double Calculate(IShape shape)
    return shape.CalculateArea();
}
class Program
{
  static void Main()
  {
    ...
  }
}

설명 문제

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

내 답:
단일 책임 원칙 - 각 클래스는 하나의 기능을 가지며, 그 클래스가 제공하는 기능은 그 하나의 기능에 대한 책임을 져야 한다는 원칙입니다.
개방 폐쇄 원칙 - 확장에는 열려있고, 변경에는 닫혀있어야 한다는 원칙입니다.
리스코프 치환 원칙 - 하위 타입은 상위 타입을 대체할 수 있어야 한다는 원칙입니다.
인터페이스 분리 원칙 - 클래스에 사용되지 않는 인터페이스를 구현하지 말아야 한다는 원칙입니다.
의존성 역전의 원칙 - 상위 클래스는 하위 클래스에 의존해서는 안 되며, 하위 클래스가 상위 클래스에 의존해야 한다는 원칙입니다.

모범 답:

  • 다섯 개의 원칙을 차례로 언급하고, 간단하게 설명을 하면 충분합니다.
    • 단일 책임 원칙 (SRP)
    • 개방-폐쇄 원칙 (OCP)
    • 리스코프 치환 원칙 (LSP)
    • 인터페이스 분리 원칙 (ISP)
    • 의존 역전 원칙 (DIP)

SOLID 원칙이란 객체지향 프로그래밍을 하면서 유지, 보수, 확장성을 향상시키는 방향으로 개발을 해 나가기를 권장하는 방법이다. 총 5가지의 원칙들의 집합으로 구성되어있고, 각각의 이니셜을 가져와서 SOLID라는 명칭을 가지게 되었다. 각각의 원칙들에 대해서 설명해보자면

  • Single Responsibility Principle (SRP) : 모듈이라고 지칭하는 것들이 각자 하나의 역할, 하나의 책임만 가져야한다. 만약 하나의 모듈이 두 개 이상의 역할을 맡는다면 분할해서 두 개의 모듈로 만들어두는것이 관리에 용이하다.
  • Open-Closed Principle (OCP) : 수정에는 닫혀있고, 확장에는 열려있어야한다. 개발하면서 추가 구현 기능이 생길 시, 기존에 작업했던 결과들을 수정하면서 작업하는 상황은 추후에 여러 문제들을 야기할 가능성이 높아지기때문에 기존 결과물의 수정을 최소화하면서 확장을 권장하는 방법으로 여겨진다.
  • Liskov Substitution principle (LSP) : 클래스 상속을 활용해서 개발을 진행할 때, 클래스가 하위 타입으로 변경되어도 상위타입에서 정의된 메서드들을 사용할 수 있어야한다. 예시를 들어보면 어떤 메서드의 매개변수로 클래스 타입이 들어갈 때, 자식 클래스로 대체되어도 사용은 가능해야한다는 말이다.
  • Interface Segregation Principle (ISP) : 클래스와 달리 인터페이스는 특정 기능들을 명시적으로 담당한다는 특징이 있다. 이 기능을 십분 활용한다면 목적과 용도에 맞춘 인터페이스의 접근으로 불필요한 간섭을 최소화 할 수 있다.
  • Dependency Inversion Principle (DIP) : 일반적으로 클래스의 계층구조를 생각하면 부모 클래스를 상속받아 자식 클래스에서 자세한 내용들을 구현할 수 있다. 하지만 클래스가 불러들여지는 경우에는 자식이 아닌 부모클래스를(혹은 인터페이스를) 호출함으로서 보다 높은 수준의 추상화를 사용하기를 권장하는 원칙이다.

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

내 답:
다형성이 잘 적용되면 동일한 이름의 메서드가 서로 다른 객체에 따라 다르게 동작 할 수 있기 때문에 코드의 재사용성이 올라간다는 장점이 있습니다.

모범 답:

  • 다형성에 대한 설명과 함께, 구현 방법에 대해 인터페이스를 곁들여 이야기해주면 정말 좋습니다.
  • 다형성의 장점은 코드 재사용, 유연성, 유지보수성에 대해 언급해주시면 좋습니다.

다형성 : 기존 코드의 재사용성과 유연성을 활용하여 다른 객체들을 생성할 수 있다는 개념
장점: 기존 코드의 수정을 최소화하며 새로운 클래스나 메서드들을 추가할 수 있다, 가독성을 높이며 의존성을 줄일 수 있다.

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

내 답:
override는 상위 클래스에 있는 메서드를 하위 클래스에서 새로운 코드를 덮어 씌우는 것 입니다.
overload는 같은 이름의 메서드가 매개변수를 어떻게 받느냐에 따라 분리되어 있는 형태입니다.

모범 답:

  • override의 경우 상속에 있어 메서드 재정의라는 단어로 설명해주시면 좋습니다.
  • 추가적으로 가상 함수에 대해 함수 테이블(Method Table) 또는 가상 테이블(Virtual Table)을 같이 언급하여 설명해주시면 좋습니다.
  • overload의 경우 동일한 메서드 이름을 사용한다는 특징을 언급해주시는 것이 좋습니다. 다형성을 같이 언급하면 정말 좋습니다.

overload : 서로 다른 형, 인자를 받는 동일한 이름의 메서드를 정의할 때 주로 사용, 해당 클래스 내에서 클래스의 이름이 아닌 출력 자료형, 매개변수 종류에 따라 구분
override : 부모클래스에서 abstract나 virtual로 작성된 메서드들을 자식 클래스에서 재정의 할 때 사용되는 키워드, 기존 메서드 위에 올라탄다(ride)는 느낌으로 받아들이면 편함

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

내 답:
클래스 외부에서 클래스의 메서드처럼 사용할 수 있는 새로운 메서드를 만드는 기능입니다.
확장 클래스를 선언할 때 static으로 선언을 해줍니다.
확장 메서드를 만들 때도 static으로 선언을 해줍니다.
확장 메서드의 첫 번째 매개변수는 this 키워드를 사용한 후 확장하고자 하는 클래스 타입을 적어 줍니다.

모범 답:

  • 코드 재사용과 유지보수성을 위해 기존 클래스의 메서드를 확장한다고 설명해주면 좋습니다.
  • 사용법의 경우 this 키워드와 함께 설명해주세요.

확장 메서드: 기존 클래스(A라고 지칭)에서 정의되어있지 않지만 외부 클래스(B라고 지칭)에서 정의하여 마치 A클래스의 메서드처럼 사용할 수 있는 기법
큰 특징으로는 static을 활용한 클래스 접근이 다분하기에 자주 사용되는 기법은 아님

실습 문제

💡 [Array에 기능 추가하기]

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

이 확장 메서드는 CalculateAverage라는 이름을 가지며, int[] 타입의 배열을 입력으로 받아 평균값을 double 타입으로 반환해야 합니다.

using System;

namespace ArrayExtensions
{
    public static class ArrayExtension
    {
        public static double CalculateAverage(this int[] array)
        {
            // TODO: 확장 메서드 CalculateAverage를 구현하세요
            
            //int i = 0;
            //foreach (int j in array)
            //{
            //    i += j;
            //}
            //return i / array.Length;
            
            double sum = 0;
			for(int i = 0; i < array.Length; i++)
			{
				sum += array[i];
			}
			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}");
        }
    }
}

profile
스터디 로그

0개의 댓글

관련 채용 정보