CodeGym /행동 /C# SELF /계층 구조 만들 때 추상화

계층 구조 만들 때 추상화

C# SELF
레벨 22, 레슨 3
사용 가능

1. 들어가기

추상화를 새의 시야에서 한 번 보자: 큰 사무실을 상상해봐, 여러 직원이 각자 다른 일을 하고 있어. 다들 공통 직책이 있을 수 있지 — "직원", 근데 각자 맡은 일은 달라. 근데 네가 사장이라면, 모든 직원이 "일하기"만 하면 되는 거야. 그게 어떻게 되는지는 신경 안 써: 프로그래머는 코드 쓰고, 회계는 보고서 찍고. 이런 "모두를 위한 공통 계약은 추상화로, 세부는 전문가에게"라는 아이디어가 제대로 된 클래스 계층 구조의 핵심이야.

코드로 보면?

모든 건 기본 추상 클래스에서 시작해. 이 클래스는 모든 자식이 꼭 해야 하는 걸 정의하지.


public abstract class Employee
{
    public string Name { get; }
    public Employee(string name)
    {
        Name = name;
    }

    // 추상 메서드 — 모든 직원이 따라야 하는 계약
    public abstract void DoWork();
}

그 다음은 파생 클래스(자식) — 프로그래머랑 회계:


public class Programmer : Employee
{
    public Programmer(string name) : base(name) { }
    public override void DoWork()
    {
        Console.WriteLine($"{Name} 코드 작성 중");
    }
}

public class Accountant : Employee
{
    public Accountant(string name) : base(name) { }
    public override void DoWork()
    {
        Console.WriteLine($"{Name} 돈 계산 중");
    }
}

이제 봐봐: 직원 리스트를 만들 수 있어 — 회계든 프로그래머든, 뭐든 추가 가능(추상화는 이런 거 완전 환영이야!).


Employee[] office = new Employee[]
{
    new Programmer("바샤"),
    new Accountant("타냐")
};

foreach (Employee emp in office)
{
    emp.DoWork(); // 각자 자기 일 함
}

바샤 코드 작성 중
타냐 돈 계산 중

봐봐, 얼마나 깔끔하고 범용적이고, 무엇보다 확장성 좋아졌는지. 새 클래스, 예를 들어 Manager 추가해도 기존 코드는 손댈 필요 없어!

2. 추상화 기반 계층 구조 만들기

거의 모든 OOP 교재에 동물 예제가 빠지지 않으니까, 우리도 전통을 따라가자.

추상 클래스 — "큰 독재자" 역할


public abstract class Animal
{
    public string Name { get; set; }
    public Animal(string name)
    {
        Name = name;
    }

    // 모든 동물은 소리 낼 수 있어야 함
    public abstract void MakeSound();

    // 근데 다 날 수 있는 건 아니니까:
    public virtual void Fly()
    {
        Console.WriteLine("나는 못 날아.");
    }
}

자식 클래스: 소리 내기 구현, 다른 메서드도 오버라이드 가능


public class Cat : Animal
{
    public Cat(string name) : base(name) { }
    public override void MakeSound()
    {
        Console.WriteLine($"{Name}: 야옹!");
    }
}

public class Dog : Animal
{
    public Dog(string name) : base(name) { }
    public override void MakeSound()
    {
        Console.WriteLine($"{Name}: 멍멍!");
    }
}

public class Eagle : Animal
{
    public Eagle(string name) : base(name) { }
    public override void MakeSound()
    {
        Console.WriteLine($"{Name}: 끼익!");
    }

    public override void Fly()
    {
        Console.WriteLine($"{Name} 하늘을 날아!");
    }
}

이제 여러 동물로 컬렉션 만들어보자:


Animal[] zoo = new Animal[]
{
    new Cat("바르식"),
    new Dog("샤릭"),
    new Eagle("오렐")
};

foreach (Animal animal in zoo)
{
    animal.MakeSound();
    animal.Fly();
}

바르식: 야옹!
나는 못 날아.
샤릭: 멍멍!
나는 못 날아.
오렐: 끼익!
오렐 하늘을 날아!

이제 어떤 자식 클래스든 쉽게 쓸 수 있지: 컬렉션에 뭐가 들어있는지 신경 안 써도 돼. 추상화가 "계약"을 챙기고, 구현 세부는 각 클래스가 알아서 해.

3. 여러 단계의 추상화

추상화는 단순히 기본 클래스랑 그 자식만 나누는 게 아니야. 복잡한 시스템에선 여러 단계로 추상화가 들어가, 한 추상 클래스가 또 다른 추상 클래스를 기반으로 만들어지기도 해. 이건 마치 레이어 케이크(아니면 슈렉이 말한 양파) 같아: 각 레이어가 불필요한 디테일을 숨기고 필요한 것만 남겨.

예시: 탈것 계층 구조


.              Vehicle (abstract)
            /           |           \
       Car           Airplane      Boat
  ElectricCar    JetAirplane    SailBoat

여기서 Vehicle이 공통 원칙(예: Move() 메서드)을 정하지만, 자동차나 비행기가 어떻게 움직이는지는 몰라. 그건 자식 클래스가 정하지. 더 구체적인 클래스, 예를 들어 ElectricCar나 JetAirplane은 자기만의 디테일을 추가해서 기능을 확장할 수 있어.

계층 구조 블록도:


.       +------------------+
        |  Vehicle         |  <--- 추상 클래스
        +------------------+
        /        \
   +-------+   +-------+
   |  Car  |   | Boat  |  <--- 중간 추상 또는 구체 클래스
   +-------+   +-------+

코드: 기본 추상 클래스


public abstract class Vehicle
{
    public string Model { get; }
    public Vehicle(string model)
    {
        Model = model;
    }

    // 추상 메서드
    public abstract void Move();
}

중간 추상 클래스

가끔 중간 단계에도 추상화가 필요해!


public abstract class Car : Vehicle
{
    public Car(string model) : base(model) { }

    public override void Move()
    {
        Console.WriteLine($"{Model} 도로를 달려.");
    }

    // 추상 메서드 — 모든 차가 전기차는 아니니까:
    public abstract void RefuelOrCharge();
}

구체 구현


public class ElectricCar : Car
{
    public ElectricCar(string model) : base(model) { }

    public override void RefuelOrCharge()
    {
        Console.WriteLine($"{Model} 전기로 충전 중.");
    }
}

public class GasolineCar : Car
{
    public GasolineCar(string model) : base(model) { }

    public override void RefuelOrCharge()
    {
        Console.WriteLine($"{Model} 휘발유 주유 중.");
    }
}

결과: 여러 단계의 추상화 덕분에 새 클래스나 기능 추가가 쉬워져.

시각화: 클래스 계층 예시

클래스 타입 추상 부모 특별 동작
Vehicle 기본 예 - 추상 메서드 Move()
Car 중간 예 Vehicle 추상 RefuelOrCharge()
ElectricCar 최종 아니오 Car RefuelOrCharge() 구현
GasolineCar 최종 아니오 Car RefuelOrCharge() 구현
Boat 최종 아니오 Vehicle Move() 구현

4. 확장 가능한 앱을 위한 추상화 활용

실전에서 좋은 점:

  1. 코드 유연성 & 확장성: 새 클래스(예: 동물, 탈것, 직원 등) 추가해도 기존 코드 안 건드려도 돼. 루프/메서드는 새 객체도 자동으로 처리 — 추상화에서 정의한 메서드만 구현하면 돼.
  2. 코드 중복 최소화: 공통 속성/메서드(예: 이름, 기본 로직)는 추상 클래스에 한 번만 정의, 자식이 자동 상속.
  3. 대형 프레임워크/시스템에서 흔히 씀: 예를 들어 ASP.NET MVC엔 추상 기본 컨트롤러(ControllerBase)가 있고, WinForms엔 모든 UI 요소의 추상 클래스 Control이 있어. 이 덕분에 프레임워크 확장해도 기존 코드 안정성 유지 가능.

어디서 쓸까?

  • 모든 대형 시스템: 은행 앱(직원, 작업, 트랜잭션), 게임(게임 엔티티, 캐릭터), 비즈니스 앱(카탈로그, 상품, 사용자), 계층적/구조적 데이터 등.
  • 기술 면접: 추상화 기반 클래스 설계/계층 설명은 면접 단골 질문.
  • 프로그램 아키텍처: 추상화로 아키텍처 "뼈대"를 미리 만들 수 있어 — 팀 개발, TDD(테스트 주도 개발), 유지보수에 딱임.

5. 계층 구조 만들 때 흔한 실수와 함정

실수 1: 추상 메서드 구현 빠뜨림.
기본 클래스의 모든 추상 멤버를 자식에서 구현 안 하면 컴파일러가 인스턴스 생성 못 하게 막아. 이건 꼭 지켜야 하는 규칙 — 구체 클래스를 만들고 싶으면 다 구현해야 해.

실수 2: 추상 클래스 다중 상속 시도.
C#에선 여러 클래스를 동시에 상속 못 해, 다 추상 클래스여도 마찬가지. 이건 언어 제한이야. 여러 소스에서 동작을 "상속"하고 싶으면 인터페이스 써. 인터페이스는 추상 클래스랑 비슷한데, 구현 없이 더 가벼워.

실수 3: 중간 추상 클래스 의미를 모름.
이런 클래스는 공통 로직 묶거나 "덜 정의된" 객체 생성을 막으려고 자주 써. 예를 들어 Car는 추상 클래스로 두면, 그냥 "차"만 만들고 구체적으로 휘발유/디젤/전기차인지 안 정하는 걸 막을 수 있어.

2
과제
C# SELF, 레벨 22, 레슨 3
잠금
추상 클래스와 클래스 계층 구조 만들기
추상 클래스와 클래스 계층 구조 만들기
2
과제
C# SELF, 레벨 22, 레슨 3
잠금
다단계 추상화: 운송 수단
다단계 추상화: 운송 수단
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION