본문 바로가기

Programming

추상화 - 추상클래스와 인터페이스 : 쉽고 재미있는 C# Programming 의 기본

 

C#에서 추상화는 주로 추상 클래스와 인터페이스를 통해 구현됩니다.

 

1. 개념

 

추상 클래스 ( Abstract Class )

 

추상 클래스는 하나 이상의 추상 멤버 (메서드, 속성 등)를 포함할 수 있는 클래스입니다.

추상 멤버는 본체가 없으며 파생 클래스(자식 클래스)에서 반드시 구현되어야 합니다.

일반적인 메서드나 속성도 포함할 수 있습니다.

 

정의되지 않은 불완전한 ( == 중괄호 { } 가 없는 ) 메서드 (추상 메서드)를 1개라도 포함하고 있는 클래스입니다.

구현은 반드시 파생 클래스(자식 클래스)에서 구현해야 합니다. 가상 메서드 (Virtual Method)와 비슷하지만 반드시 구현해야 하므로 virtual 키워드는 붙이지 않습니다.

 

[요약]

  • 추상 클래스는 하나 이상의 추상 멤버(abstract method, abstract property)를 가지고 있는 클래스입니다.
  • 추상 멤버는 선언만 있고, 실제 구현은 없는 메서드 또는 속성을 말합니다.
  • 추상 클래스는 직접 객체를 생성할 수 없으며, 반드시 상속하여 사용해야 합니다.

[추상 멤버]

  • 추상 클래스는 일반 메서드와 속성 뿐만 아니라, 추상 메서드와 추상 속성도 포함할 수 있습니다.
  • 추상 메서드와 속성은 파생 클래스(자식 클래스)에서 반드시 구현되어야 합니다.

2. 형식

abstract class Shape
{
    // 추상 메서드
    public abstract void Draw();

    // 일반 메서드
    public void Resize()
    {
        Console.WriteLine("Resizing the shape.");
    }

    // 추상 속성
    public abstract int Area { get; }
}

 

추상 클래스 ShapeDraw라는 추상 메서드, Resize라는 일반 메서드, 그리고 Area라는 추상 속성을 가지고 있습니다.

 

3. 상속 및 구현

class Circle : Shape
{
    private int radius;

    public Circle(int r)
    {
        radius = r;
    }

    // 추상 메서드의 구현
    public override void Draw()
    {
        Console.WriteLine("Drawing a circle.");
    }

    // 추상 속성의 구현
    public override int Area
    {
        get { return (int)(Math.PI * radius * radius); }
    }
}

 

Circle 클래스는 Shape 클래스를 상속받았고, 추상 클래스의 추상 메서드인 Draw와 추상 속성인 Area를 반드시 구현해야 합니다. 여기서 일반메서드인 Resize 메서드는 구현하지 않아도 됩니다.

 

4. 사용 예

Shape myShape = new Circle(5);
myShape.Draw();    // Circle 클래스의 Draw 메서드 호출
myShape.Resize();  // Shape 클래스의 Resize 메서드 호출
int area = myShape.Area;  // Circle 클래스의 Area 속성 호출

 

Shape 클래스의 인스턴스를 Circle 클래스로 생성했습니다.

이렇게 생성한 객체는 Shape 형식으로 선언되었기 때문에 Draw, Resize, Area 등의 메서드와 속성에 접근할 수 있습니다.

Draw 메서드는 Circle 클래스의 구현이 호출되고, Resize 메서드는 Shape 클래스의 구현이 호출됩니다.

 

5. 주의사항

  • 추상 클래스는 직접 객체를 생성할 수 없으므로 추상 클래스 타입의 변수로 파생 클래스의 객체를 참조할 수 있습니다.
  • 추상 클래스는 반드시 상속 후에 사용되어야 하며, 파생 클래스에서 추상 멤버를 반드시 구현해야 합니다.
  • 추상 클래스 내에서는 일반 메서드를 가질 수 있습니다.
  • 파생 클래스에서 추상 멤버를 구현하지 않으면, 그 파생 클래스도 추상 클래스로 선언되어야 합니다.
    다시 말해, 추상 클래스에 있는 모든 추상 멤버는 파생 클래스에서 반드시 구현되어야 하며, 만약 구현되지 않으면 그 클래스는 추상클래스로 선언되어야 합니다.

6. 장점

  • 공통된 기능을 추상 클래스에 구현함으로써 코드 중복을 피할 수 있습니다.
  • 파생 클래스에서 특정 동작을 강제하고, 다형성을 지원하여 유연한 구조를 제공합니다.
  • 인터페이스와 함께 사용하여 다중 상속을 구현할 수 있습니다.

추상 클래스는 일반 클래스와 달리 인스턴스를 직접 생성할 수 없고, 파생 클래스를 통해 사용되므로 상속 과계에서 코드의 일관성과 유지보수성을 높이는 데 도움이 됩니다.

 

추상 클래스를 사용하면 공통된 동작을 가진 클래스를 일반화하여 코드의 재사용성을 높일 수 있습니다. 상속을 통해 특정 동작을 강제하고, 다형성을 지원하여 유연한 구조를 제공할 수 있습니다.


 

인터페이스 ( Interface )

 

1. 개념

 

인터페이스는 C#에서 여러 클래스 간에 공통된 동작을 정의하고 다중 상속을 지원하기 위한 도구입니다.

  • 인터페이스는 추상 메서드, 속성, 이벤트 등의 추상 멤버들로만 구성된 추상 형식입니다.
  • 클래스가 인터페이스를 구현하면, 해당 클래스는 인터페이스에 정의된 모든 멤버를 구현해야 합니다.

2. 형식

interface IDrawable
{
    void Draw(); // 추상 메서드
}

interface IMovable
{
    void Move(); // 추상 메서드
}

* 인터페이스의 이름은 통상 이름앞에 대문자 I 를 사용합니다.

 

인터페이스는 interface 키워드를 사용하여 정의되며, 추상 메서드를 선언합니다. 여기서는 IDrawableIMovable이라는 두 개의 인터페이스를 정의했습니다.

 

3. 구현

 

클래스에서 인터페이스를 구현할 때는 class 키워드 뒤에 콜론(:)을 사용하여 구현합니다.

구현되어야 하는 각 메서드에 대해 public 접근 제한자를 사용하고, 메서드 바디 대신 세미콜론(;)을 사용하여 추상 메서드임을 표시합니다.

class Circle : IDrawable, IMovable
{
    private int radius;

    public Circle(int r)
    {
        radius = r;
    }

    public void Draw()
    {
        Console.WriteLine("Drawing a circle.");
    }

    public void Move()
    {
        Console.WriteLine("Moving the circle.");
    }
}

 

위의 코드에서 Circle 클래스는 IDrawableIMovable 인터페이스를 모두 구현하고 있습니다.

Draw 메서드와 Move 메서드를 구현하여 인터페이스에서 정의한 추상 메서드에 대한 실제 동작을 제공하고 있습니다.

 

4. 사용 예

Circle myCircle = new Circle(5);
myCircle.Draw(); // IDrawable 인터페이스의 Draw 메서드 호출
myCircle.Move(); // IMovable 인터페이스의 Move 메서드 호출

 

Circle 클래스의 인스턴스를 생성하고, 이를 통해 IDrawableIMovable 인터페이스의 메서드를 호출할 수 있습니다. 이렇게 인터페이스를 구현함으로써, 여러 클래스가 동일한 메서드 이름을 사용하면서도 각각의 특별한 동작을 수행할 수 있습니다.

 

5. 주의사항

  • 클래스는 여러 개의 인터페이스를 동시에 구현할 수 있습니다.
  • 인터페이스는 다중 상속을 지원하므로, 여러 인터페이스의 특성을 하나의 클래스에서 모두 사용할 수 있습니다.
  • 인터페이스는 클래스의 행동을 정의하고 클래스 간에 표준을 제공하는 데 사용됩니다.

6. 장점

  • 다중 상속의 대안: C#에서는 클래스가 다중 클래스 상속을 지원하지 않지만, 여러 개의 인터페이스를 구현할 수 있습니다. 클래스가 여러 인터페이스를 구현함으로써, 다양한 특성을 갖추게 됩니다.
  • 유연한 설계: 인터페이스를 사용하면 클래스 간의 결합도를 낮추고, 각 클래스를 독립적으로 변경하거나 확장할 수 있습니다. 이는 소프트웨어의 유연성을 향상시킵니다.
  • 표준화된 규약 제공: 인터페이스는 일종의 계약(contract)으로서, 해당 인터페이스를 구현한 클래스들은 특정한 규약을 따르게 됩니다. 이를 통해 코드의 일관성을 유지하고, 협업에서 표준을 정의할 수 있습니다.

 

[ 추상클래스와 인터페이스 비교 ]

 

1. 공통점

 

본체가 정의되지 않는 (선언만 있고 구현내용이 없는) 추상 메서드만 갖습니다.

구체적인 구현은 숨기고, 공통된 기능을 정의합니다.

  • 추상 멤버를 가질 수 있다: 추상 클래스와 인터페이스는 모두 추상 멤버(추상 메서드, 속성 등)를 가질 수 있습니다. 이는 파생 클래스 또는 구현 클래스에서 반드시 구현되어야 합니다.
  • 다형성을 제공한다: 추상 클래스와 인터페이스는 다형성을 지원하여, 같은 타입으로 여러 클래스 또는 구현체를 다룰 수 있게 합니다.
  • 객체 지향 프로그래밍(OOP)의 핵심 개념: 추상 클래스와 인터페이스는 객체 지향 프로그래밍에서 추상화를 통해 코드의 재사용성을 높이고, 코드를 구조화하는 데 사용됩니다.

2. 차이점

 

추상클래스는 인스턴스를 직접 생성할 수 없고, 추상 클래스의 인스턴스를 만들기 위해서는 이 클래스를 상속받은 파생클래스를 생성하고, 그 클래스의 인스턴스를 생성해야 합니다.

인터페이스는 인스턴스를 직접 생성할 수 없고, 구현이 필요한 멤버들을 선언할 뿐입니다.

 

추상클래스는 추상 메서드를 선택적으로 가지는데 비해, 인터페이스는 전부 추상메소드만 가집니다.

그래서 인터페이스를 상속한 클래스는 메소드를 전부 구현해야 합니다.

 

2.1 구현 유무:

  • 추상 클래스: 일반 메서드 또는 속성 등을 구현할 수 있습니다. 추상 클래스는 일부 구현된 멤버를 가질 수 있습니다.
  • 인터페이스: 추상 멤버만을 가지며, 멤버의 구현이 없습니다. 클래스는 인터페이스를 구현할 때 모든 멤버를 구현해야 합니다.

2.2 다중 상속: 

  • 추상 클래스: C#에서는 클래스가 하나의 클래스만을 상속할 수 있습니다. 다중 상속을 지원하지 않습니다.
  • 인터페이스: 여러 개의 인터페이스를 구현함으로써 다중 상속과 유사한 효과를 얻을 수 있습니다.

2.3 생성자의 존재:

  • 추상 클래스: 생성자를 가질 수 있으며, 객체를 초기화하는 역할을 수행할 수 있습니다.
  • 인터페이스: 생성자를 가질 수 없습니다. 객체의 초기화에 관련된 작업을 수행할 수 없습니다.

2.4 접근 한정자 사용:

  • 추상 클래스: 접근 한정자를 사용하여 멤버의 가시성을 제어할 수 있습니다.
  • 인터페이스: 멤버는 기본적으로 public이며, 접근 한정자를 명시하지 않습니다.

2.5 설계 목적:

  • 추상 클래스: 코드의 일부를 공유하고, 강제로 구현해야 하는 메서드를 제공하기 위해 사용됩니다.
  • 인터페이스: 클래스 간에 공통된 동작을 정의하고, 여러 클래스에 대한 표준을 제공하기 위해 사용됩니다.

 

[ 간단한 예제를 통해서 쉽게 이해해 보겠습니다. ]

 

일반적으로 추상 클래스는 서로 다른 클래스 간의 공통된 기능을 구현할 때 사용되고, 인터페이스는 서로 관계없는 클래스 간에 공통된 동작을 정의할 때 사용됩니다.

 

1. 추상 클래스의 사용 예

// 추상 클래스
abstract class Smartphone
{
    // 공통된 기능 - 구현이 있는 메서드
    public void PowerOn()
    {
        Console.WriteLine("Smartphone is powering on.");
    }

    // 추상 메서드 - 각 회사에서 반드시 구현해야 함
    public abstract void Call();

    public abstract void SendText();

    public abstract void UseInternet();
}

// 추상 클래스를 상속받은 구현 클래스 - Samsung
class SamsungPhone : Smartphone
{
    public override void Call()
    {
        Console.WriteLine("Samsung phone is making a call.");
    }

    public override void SendText()
    {
        Console.WriteLine("Sending a text from Samsung phone.");
    }

    public override void UseInternet()
    {
        Console.WriteLine("Browsing the internet on Samsung phone.");
    }
}

// 추상 클래스를 상속받은 구현 클래스 - Apple
class IPhone : Smartphone
{
    public override void Call()
    {
        Console.WriteLine("iPhone is making a call.");
    }

    public override void SendText()
    {
        Console.WriteLine("Sending a text from iPhone.");
    }

    public override void UseInternet()
    {
        Console.WriteLine("Browsing the internet on iPhone.");
    }
}

 

위의 예제에서 Smartphone 추상 클래스는 PowerOn이라는 구현이 있는 메서드와 Call, SendText, UseInternet라는 추상 메서드를 정의합니다.

 

SamsungPhone 클래스와 IPhone 클래스는 각각 Smartphone을 상속받아 추상 메서드를 구현하고 있습니다.

 

2. 인터페이스 사용 예

// 인터페이스
interface ICommunication
{
    void Call();
    void SendText();
    void UseInternet();
}

// 각 회사에서 인터페이스를 구현하는 클래스
class SamsungPhone : ICommunication
{
    public void Call()
    {
        Console.WriteLine("Samsung phone is making a call.");
    }

    public void SendText()
    {
        Console.WriteLine("Sending a text from Samsung phone.");
    }

    public void UseInternet()
    {
        Console.WriteLine("Browsing the internet on Samsung phone.");
    }
}

class IPhone : ICommunication
{
    public void Call()
    {
        Console.WriteLine("iPhone is making a call.");
    }

    public void SendText()
    {
        Console.WriteLine("Sending a text from iPhone.");
    }

    public void UseInternet()
    {
        Console.WriteLine("Browsing the internet on iPhone.");
    }
}

 

이번에는 ICommunication 인터페이스를 사용하여 통화, 문자, 인터넷 기능을 정의하고, 각 회사의 스마트폰 클래스에서 이 인터페이스를 구현하는 예제입니다.

SamsungPhone 클래스와 IPhone 클래스는 각각 ICommunication을 구현하여 필요한 기능을 제공합니다.

두 예제는 각각 추상 클래스와 인터페이스를 사용하여 공통된 스마트폰 기능을 정의하고 각 회사에서 이를 구현하는 방법을 보여줍니다.

 

3. 추상 클래스와 인터페이스의 특징

 

3.1 추상 클래스의 특징

 

  • 일부 구현이 가능한 멤버 제공: 추상 클래스는 일부 구현이 있는 메서드나 속성을 가질 수 있습니다. PowerOn과 같이 이미 구현이 된 메서드가 있습니다.
  • 단일 상속: 클래스는 하나의 추상 클래스만을 상속할 수 있습니다.
  • 생성자 존재: 추상 클래스는 생성자를 가질 수 있습니다.
  • 필드 및 일반 메서드 정의 가능: 추상 클래스는 필드나 일반 메서드를 포함할 수 있습니다.

3.2 인터페이스의 특징

 

  • 모든 멤버가 추상: 인터페이스는 모든 멤버가 추상이므로 구현이 없습니다. 메서드, 속성, 이벤트 등을 선언만 합니다.
  • 다중 상속: 클래스는 여러 개의 인터페이스를 구현할 수 있습니다.
  • 생성자 없음: 인터페이스는 생성자를 가질 수 없습니다.
  • 멤버의 가시성: 인터페이스의 멤버는 기본적으로 public이며, 가시성을 명시하지 않습니다.
  • 추가 기능 제공 가능: 클래스에서 여러 인터페이스를 구현하여 여러 기능을 추가로 제공할 수 있습니다.

위의 차이점을 기반으로 스마트폰 예제에서는 추상 클래스가 이미 구현된 PowerOn 메서드를 가지고 있고, 생성자도 존재합니다. 인터페이스는 모든 메서드가 추상이며 생성자가 없습니다. 클래스에서는 여러 인터페이스를 구현하여 다양한 기능을 추가할 수 있습니다.

 

4. 추상 클래스와 인터페이스 사용 이점

 

4.1 추상 클래스의 이점

 

  • 부분적인 구현 제공: 추상 클래스는 일부 메서드를 구현할 수 있기 때문에, 공통된 로직을 추상 클래스에서 구현하고 파생 클래스에서는 특화된 부분만 구현할 수 있습니다. 예를 들어, 모든 스마트폰이 공통으로 가져야 하는 PowerOn 메서드를 구현할 수 있습니다.
  • 공통 필드와 메서드 제공: 추상 클래스는 필드와 메서드를 가질 수 있어서, 공통된 필드나 메서드를 제공하여 코드의 재사용성을 높일 수 있습니다.
  • 단일 상속: 추상 클래스는 하나의 클래스만을 상속받을 수 있습니다. 이는 클래스 간의 강력한 관계를 형성할 수 있습니다.

4.2 인터페이스의 이점

 

  • 다중 상속: 인터페이스는 여러 개의 인터페이스를 구현할 수 있습니다. 클래스가 다양한 동작을 수행해야 할 때, 인터페이스를 통해 다중 상속과 유사한 효과를 얻을 수 있습니다. 각 회사에서 제공하는 다양한 기능을 하나의 클래스에서 구현할 수 있습니다.
  • 유연한 설계: 인터페이스는 클래스 간에 결합도를 낮추어 유연한 설계를 가능하게 합니다. 각 클래스는 필요에 따라 인터페이스를 구현하여 필요한 동작을 정의할 수 있습니다.
  • 표준화된 규약 제공: 인터페이스는 특정한 규약을 정의하여 해당 규약을 따르는 클래스는 특정한 동작을 보장합니다. 이는 협업이나 라이브러리 개발에서 표준을 제공하는 데 도움이 됩니다.

4.3 사용 예제에 기반한 이점

 

4.3.1 추상 클래스 사용 이점:

  • Smartphone 추상 클래스에서 PowerOn 메서드를 구현하여 모든 스마트폰에서 필요한 초기화 로직을 제공할 수 있습니다.
  • Smartphone 클래스의 필드를 통해 공통된 데이터를 저장하고, 이를 상속받는 클래스에서 공유할 수 있습니다.

4.3.2 인터페이스 사용 이점:

  • ICommunication 인터페이스를 통해 각 스마트폰이 Call, SendText, UseInternet라는 공통된 동작을 가지게 하여, 클래스 간에 표준을 제공합니다.
  • 각 스마트폰 회사는 ICommunication 인터페이스를 구현하여 독립적으로 기능을 추가할 수 있습니다.

 

추상 클래스와 인터페이스는 C#에서 객체 지향 프로그래밍에서 중요한 개념 중 하나입니다.

이들은 코드의 재사용성과 유연성을 높이는데 기여하며, 특히 다양한 클래스 간의 관계를 정의하고 코드의 일관성을 유지하는 데 중요한 역할을 합니다.

 

추상 클래스는 부분적인 구현을 제공함으로써 공통된 로직을 추상화하고, 단일 상속을 통해 클래스 간의 강력한 관계를 형성합니다. 이는 코드의 구조를 일관되게 유지하고 재사용 가능한 코드를 작성하는 데 도움이 됩니다.

 

인터페이스는 클래스 간의 결합도를 낮추고, 다중 상속을 통해 여러 개의 인터페이스를 구현할 수 있어 유연한 설계를 가능케 합니다. 특히 표준화된 규약을 제공하여 클래스 간의 통일된 동작을 정의하는 데 사용됩니다.

 

이러한 개념들은 프로그램의 확장성과 유지보수성을 향상시키는데 기여하며, 팀 프로젝트에서 협업 시 일관성 있는 코드를 작성하는데 큰 도움을 줍니다. 따라서 C#에서는 추상 클래스와 인터페이스를 적절히 활용하여 객체 지향적인 설계를 할 수 있도록 고려하는 것이 중요합니다.