본문 바로가기
프로그래밍/C#

[C#] 추상클래스와 인터페이스의 비교 및 사용처 분석

by webcodur 2024. 4. 26.
728x90
반응형

 

목차

     

    본 포스팅은 C#에서 인터페이스와 추상 클래스를 사용하는 방법을 다룬다. 우선 인터페이스와 추상 클래스는 상세한 설명 없이 예시로만 간단히 작성하였고, 자세한 사항을 두 개념의 비교 부분에서 자세히 다룬다. 이를 통해 각각의 기능을 정확히 이해하고 언제 사용하면 적합할지 학습할 수 있다.

     

    1. 인터페이스 사용법

    C#에서 인터페이스는 객체 지향 프로그래밍의 핵심 요소 중 하나이다. 인터페이스를 통해 다양한 클래스가 동일한 계약을 따르도록 하여, 코드의 유연성과 재사용성을 높일 수 있다. 아래 예시에서는 C# 인터페이스의 기본적인 사용법부터, 다중 인터페이스 구현, 명시적 인터페이스 구현, 그리고 인터페이스 상속에 이르기까지의 예시를 소개한다.

     

    1.1 기본 예시

    // 기본 예시
    using System;
    
    // 인터페이스 정의
    interface IAnimal
    {
        void MakeSound(); // 소리내는 행위를 정의하는 메서드
    }
    
    // Dog 클래스는 IAnimal 인터페이스를 구현
    class Dog : IAnimal
    {
        public void MakeSound()
        {
            Console.WriteLine("멍멍!");
        }
    }
    
    // Cat 클래스는 IAnimal 인터페이스를 구현
    class Cat : IAnimal
    {
        public void MakeSound()
        {
            Console.WriteLine("야옹!");
        }
    }
    
    // 메인 프로그램
    class Program
    {
        static void Main()
        {
            IAnimal dog = new Dog();
            IAnimal cat = new Cat();
            dog.MakeSound(); // "멍멍!" 출력
            cat.MakeSound(); // "야옹!" 출력
        }
    }
    
    

     

    1.2. 다중 인터페이스 구현

    여러 인터페이스를 하나의 클래스에서 구현할 수 있다.

    // 다중 인터페이스 구현
    using System;
    
    // 동물과 비행이 가능한 객체를 위한 인터페이스 정의
    interface IAnimal { void MakeSound(); }
    interface IFlyable { void Fly(); }
    
    // Bird 클래스는 IAnimal과 IFlyable 인터페이스를 모두 구현
    class Bird : IAnimal, IFlyable
    {
        public void MakeSound()
        {
            Console.WriteLine("새가 지저귑니다.");
        }
    
        public void Fly()
        {
            Console.WriteLine("새가 날아갑니다.");
        }
    }
    
    // 메인 프로그램
    class Program
    {
        static void Main()
        {
            Bird bird = new Bird();
            bird.MakeSound(); // "새가 지저귑니다." 출력
            bird.Fly();       // "새가 날아갑니다." 출력
        }
    }
    

     

    1.3. 명시적 인터페이스 구현

    두 인터페이스에서 동일한 이름의 메서드를 가질 때 명시적 인터페이스 구현으로 충돌을 해결할 수 있다.

    using System;
    
    interface IDog {void Bark();}
    interface ICat {void Bark();}
    
    class Animal : IDog, ICat
    {
        void IDog.Bark() {Console.WriteLine("강아지가 짖습니다.");}
        void ICat.Bark() {Console.WriteLine("고양이가 울부짖습니다.");}
    }
    
    class Program
    {
        static void Main()
        {
            Animal animal = new Animal();
            ((IDog)animal).Bark(); 
            ((ICat)animal).Bark(); 
        }
    }
    

     

    1.4. 인터페이스 상속

    인터페이스가 다른 인터페이스를 상속할 수 있다.

    using System;
    
    interface IAnimal {void MakeSound();}
    
    interface IFlyableAnimal : IAnimal {void Fly();}
    
    class Bird : IFlyableAnimal
    {
        public void MakeSound() {Console.WriteLine("새가 지저귑니다.");}
        public void Fly() {Console.WriteLine("새가 날아갑니다.");}
    }
    
    class Program
    {
        static void Main()
        {
            Bird bird = new Bird();
            bird.MakeSound(); // "새가 지저귑니다." 출력
            bird.Fly(); // "새가 날아갑니다." 출력
        }
    }
    

     

     

    2. 추상 클래스 사용법

    추상 클래스는 C#에서 객체 지향 프로그래밍의 중요한 요소 중 하나로, 특정 클래스가 완전히 구현되지 않아 인스턴스화될 수 없는 클래스를 의미한다. 이 섹션에서는 추상 클래스의 기본 사용법부터, 상속 및 메서드 오버라이딩, 그리고 추상 클래스 내의 필드와 생성자 사용까지를 다룬다.

     

    2.1 기본 예시

    using System;
    
    // 추상 클래스 정의
    public abstract class Animal
    {
        // 추상 메서드 정의
        public abstract void MakeSound();
    
        // 일반 메서드도 포함 가능
        public void Sleep()
        {
            Console.WriteLine("잠을 잔다.");
        }
    }
    
    // Dog 클래스는 Animal 추상 클래스를 상속받음
    public class Dog : Animal
    {
        // 추상 메서드 구현
        public override void MakeSound()
        {
            Console.WriteLine("멍멍!");
        }
    }
    
    // 메인 프로그램
    class Program
    {
        static void Main()
        {
            Dog dog = new Dog();
            dog.MakeSound(); // "멍멍!" 출력
            dog.Sleep();     // "잠을 잔다." 출력
        }
    }
    

     

    2.2 상속과 메서드 오버라이딩

    추상 클래스를 상속받는 클래스는 추상 클래스에서 정의된 모든 추상 메서드를 구현해야 한다. 이를 통해 코드의 재사용성과 확장성을 보장한다.

    using System;
    
    // 추상 클래스 정의
    public abstract class Bird
    {
        public abstract void Fly();
    
        // 추상 클래스에서 일반 메서드 정의
        public void Eat()
        {
            Console.WriteLine("먹는다.");
        }
    }
    
    // Eagle 클래스는 Bird 추상 클래스를 상속받아 메서드를 구현
    public class Eagle : Bird
    {
        public override void Fly()
        {
            Console.WriteLine("독수리가 하늘을 난다.");
        }
    }
    
    class Program
    {
        static void Main()
        {
            Eagle eagle = new Eagle();
            eagle.Fly();   // "독수리가 하늘을 난다." 출력
            eagle.Eat();   // "먹는다." 출력
        }
    }
    

     

    2.3 필드와 생성자 사용

    추상 클래스는 상태를 저장할 수 있는 필드와 생성자를 포함할 수 있으며, 이는 상속받는 클래스에서 활용될 수 있다.

    using System;
    
    // 추상 클래스 정의
    public abstract class Vehicle
    {
        protected int wheels;
    
        // 생성자를 통해 필드 초기화
        public Vehicle(int numberOfWheels)
        {
            wheels = numberOfWheels;
        }
    
        // 추상 메서드
        public abstract void Start();
    
        // 일반 메서드
        public void Stop()
        {
            Console.WriteLine("차량이 멈춘다.");
        }
    }
    
    // Car 클래스는 Vehicle 추상 클래스를 상속받음
    public class Car : Vehicle
    {
        public Car() : base(4) // 차량의 바퀴 수는 4로 초기화
        {
        }
    
        public override void Start()
        {
            Console.WriteLine($"차량이 {wheels} 바퀴로 움직인다.");
        }
    }
    
    class Program
    {
        static void Main()
        {
            Car car = new Car();
            car.Start();  // "차량이 4 바퀴로 움직인다." 출력
            car.Stop();   // "차량이 멈춘다." 출력
        }
    }
    

    이 세션에서는 추상 클래스의 다양한 활용 방법을 소개하였다. 추상 클래스를 사용하면 코드의 일관성과 재사용성을 향상시킬 수 있으며, 객체 지향 설계의 유연성을 증가시킬 수 있다.

     

     

    3. 인터페이스 vs 추상 클래스 비교

    인터페이스와 추상 클래스는 유사해 보이지만 기본적인 차이가 있다. 인터페이스는 모든 메서드가 기본적으로 추상 메서드로 정의되며, 어떠한 필드도 포함할 수 없다. 반면, 추상 클래스는 추상 메서드와 일반 메서드를 모두 포함할 수 있으며, 필드도 가질 수 있다. 인터페이스는 다중 상속이 가능하지만, 추상 클래스는 단일 상속만 가능하다. 이는 인터페이스가 설계 단계에서 타입의 행동을 정의하는 데에 초점을 맞춘 반면, 추상 클래스는 일부 기능이 구현된 상태로 상속받을 수 있도록 함으로써 좀 더 구체적인 구현을 제공하기 때문이다.

     

    3.1 주요 차이점 설명

    • 인터페이스
      • 메서드: 메서드 명칭만 명시 (추상 메서드)
      • 필드: 필드를 정의할 수 없음 (상태를 저장할 수 없음)
      • 상속: 다중 상속 가능.
      • 목적: 타입의 행동을 정의
    • 추상 클래스
      • 메서드: 메서드 구현도 가능 (추상 메서드 + 일반 메서드)
      • 필드: 필드를 포함할 수 있어 상태 정보를 저장함.
      • 상속: 단일 상속만 가능.
      • 목적 : 타입의 행동과 일부 구현을 제공

     

    3.2 비교 예제

    인터페이스 예제: 다음 예시는 인터페이스를 통한 다중 상속을 구현하고 각 클래스가 인터페이스의 메서드를 어떻게 구현하는지 보여준다.

    using System;
    
    // 두 개의 인터페이스 정의
    public interface IEngine
    {
        void StartEngine(); // 엔진 시작 메서드
    }
    
    public interface ILights
    {
        void ToggleLights(); // 조명 토글 메서드
    }
    
    // Car 클래스는 IEngine과 ILights 인터페이스를 모두 구현
    public class Car : IEngine, ILights
    {
        public void StartEngine()
        {
            Console.WriteLine("Car engine started.");
        }
    
        public void ToggleLights()
        {
            Console.WriteLine("Car lights toggled.");
        }
    }
    
    class Program
    {
        static void Main()
        {
            Car myCar = new Car();
            myCar.StartEngine();  // "Car engine started."
            myCar.ToggleLights(); // "Car lights toggled."
        }
    }
    

     

     

    추상 클래스 예제: 추상 클래스에서는 필드를 포함할 수 있고, 일부 메서드에 대한 구현을 제공한다.

    using System;
    
    // 추상 클래스 정의
    public abstract class Vehicle
    {
        protected int wheelCount; // 차량의 바퀴 수를 나타내는 필드
        public Vehicle(int wheels)
        {
            wheelCount = wheels;
        }
    
        public abstract void Start(); // 추상 메서드는 파생 클래스에서 구현해야 함
    
        public void Stop() // 상세 구현이 가능
        {
            Console.WriteLine("Vehicle stopped."); // 모든 차량이 공통적으로 사용할 정지 메서드
        }
    }
    
    // 추상 클래스 상속
    public class Bus : Vehicle
    {
        public Bus() : base(6) // Bus는 6개의 바퀴를 가짐
        {
        }
    
        public override void Start()
        {
            Console.WriteLine($"Bus with {wheelCount} wheels started.");
        }
    }
    
    class Program
    {
        static void Main()
        {
            Bus myBus = new Bus();
            myBus.Start();  // "Bus with 6 wheels started."
            myBus.Stop();   // "Vehicle stopped."
        }
    }
    

     

    3.3 주 사용처

    인터페이스

    1. 다양한 클래스에서 동일한 기능이 필요할 때: 인터페이스는 다양한 클래스가 동일한 메서드를 구현하도록 강제할 수 있다. 이는 다양한 타입의 객체를 동일한 방식으로 처리할 수 있게 해준다.
    2. 모듈 간 결합도를 낮추고자 할 때: 인터페이스를 사용하면 구현 세부사항을 숨기고, 인터페이스만을 통해 상호 작용하게 할 수 있다. 이는 코드의 결합도를 낮추고 유연성을 증가시키는 데 유리하다.
    3. 다중 상속이 필요할 때: C#에서는 클래스가 여러 인터페이스를 구현할 수 있으므로, 하나의 클래스에 여러 기능을 추가하고 싶을 때 유용하다.

    추상 클래스

    1. 공통적인 기본 기능을 제공하고자 할 때: 추상 클래스를 사용하면 일부 메서드 또는 필드를 구현하여 상속받는 클래스에서 이를 사용하거나 확장할 수 있다. 이는 코드 재사용을 촉진한다.
    2. 상속 구조에서 코드를 공유하고자 할 때: 상속받는 모든 클래스에 공통적인 메서드나 필드가 있다면 추상 클래스에서 이를 구현하여 중복을 최소화할 수 있다.
    3. 클래스 계층구조에서 엄격한 규칙을 적용하고자 할 때: 추상 클래스는 특정 메서드가 반드시 구현되어야 함을 보장하며, 선택적으로 메서드의 구현을 제공할 수 있다.
    반응형

    '프로그래밍 > C#' 카테고리의 다른 글

    [C#] 딕셔너리를 이용한 조건문 리펙토링  (2) 2024.04.26