C#/C# 기본

[C#] 인터페이스와 추상 클래스

로파이 2021. 10. 7. 15:33

인터페이스 interface

    interface ILogger
    {
        void WriteLog(string message);
    }

클래스와 비슷한 인터페이스는 필드를 가지지 않으며 메서드, 이벤트, 인덱서, 프로퍼티만 가질 수 있다.

정의한 메서드가 구현부가 없어도 되며 인터페이스를 상속하는 클래스에서 반드시 해당 메서드(혹은 프로퍼티)를 구현할 것을 강제한다.

인터페이스에 접근 제한자를 사용할 수 없으며 기본적으로 public이다.

인터페이스는 인스턴스화가 불가능하고 인터페이스 메서드를 모두 구현한 파생클래스를 인스턴스화 하여 사용한다.

 

ILogger 인터페이스를 상속한 다양한 파생 클래스

    class ConsoleLogger : ILogger
    {
        public void WriteLog(string message)
        {
            Console.WriteLine("{0} {1}", DateTime.Now.ToLocalTime(), message);
        }
    }

    class FileLogger : ILogger 
    {

        private StreamWriter writer;

        public FileLogger(string path)
        {
            writer = File.CreateText(path);
            writer.AutoFlush = true;
        }

        public void WriteLog(string message)
        {
            writer.WriteLine("{0} {1}", DateTime.Now.ToShortDateString(), message);
        }
    }

void WriteLog(string message) 메서드를 구현하여 어떤 Logger인체 실체화하고 있다.

   class ClimateMonitor
    {
        private ILogger logger;
        public ClimateMonitor(ILogger logger)
        {
            this.logger = logger;
        }
        public void Start()
        {
            while(true)
            {
                Console.WriteLine("온도를 입력해주세요.");
                string temp = Console.ReadLine();
                if (temp == "")
                    break;

                logger.WriteLog("현재 온도 : " + temp);
            }
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            //ClimateMonitor monitor = new ClimateMonitor(new FileLogger("Mylog.txt"));
            ClimateMonitor monitor = new ClimateMonitor(new ConsoleLogger());

            monitor.Start();

            Console.ReadKey();
        }
    }

ClimateMonitor라는 클래스에서 ILogger 형식을 사용하여 로그를 출력한다하면 프로그래머가 선택한 Logger에 따라 인터페이스 형식의 WriteLog 메서드를 사용하게된다.

 

- 인터페이스의 인터페이스 상속과 다중 상속

클래스와 마찬가지로 인터페이스를 상속할 수 있지만 클래스는 다중 상속을 허용하지 않는다.

인터페이스의 상속은 주로 이전에 정의된 인터페이스에 새로운 기능을 추가하고 싶은데 인터페이스와 관련된 파생클래스가 많아 새로운 구현을 모두 정의하기 부담스러울 때 사용한다.

    interface IFormattableLogger : ILogger
    {
        void WriteLog(string format, params Object[] args);
    }

    class MyLogger : IFormattableLogger
    {
        public void WriteLog(string message) { /**/ }
        public void WriteLog(string format, params Object[] args)
        {
            Console.WriteLine("{0} {1}", DateTime.Now.ToLocalTime(), String.Format(format, args));
        }
    }

다중 상속의 경우 복합적인 기능을 갖는 클래스를 구체화하고 싶을 때 사용한다.

    class MyCloneLogger : ILogger, ICloneable
    {
        public void WriteLog(string message) { /**/ }
        public object Clone() { return new MyCloneLogger(); }
    }

- 인터페이스 기본 구현된 메서드

C# 8.0이상 부터는 인터페이스 메서드에 기본 구현된 메서드를 사용할 수 있다.

        void WriteError(string message)
        {
            Console.WriteLine("Error {0}", message);
        }

파생 클래스에서 해당 메서드를 재구현하지 않는다면 해당 메서드를 없는 것으로 간주하고 컴파일 에러가 발생한다.

ILogger logger = new ConsoleLogger();
logger.WriteError(); // OK

ConsoleLogger clogger = logger as ConsoleLogger;
clogger.WriteError(); // Compile Error

 

추상 클래스

추상 클래스는 인터페이스와 클래스의 사이로 인터페이스처럼 파생 클래스에서 반드시 구현해야하는 메서드를 정의할 수 있고 인스턴스화가 불가능하지만 클래스처럼 필드를 가지고 접근 제한자를 설정할 수 있다.

 

클래스의 선언에 abstract를 붙이면 그 클래스는 인스턴스화할 수 없는 추상 클래스로 취급된다. 메서드 중 하나라도 abstract 키워드가 있다면 클래스 선언을 abstract로 해야하며 그 메서드는 반드시 파생 클래스에서 오버라이딩되어야 한다.

 

abstract 메서드가 없는 클래스를 abstract로 선언할 수 있으나 인스턴스화만 금지시키고 상속을 강제하지 않기 때문에 의미가 없다.

    abstract class GameObject : ICloneable
    {
        protected string Name;
        protected Int32 Id;
        static private Int32 Count = 0;
        public GameObject(string Name) { this.Name = Name; Id = Count++; }

        // 추상 메서드
        public abstract void Awake();
        public abstract void Start();
        public abstract void Tick();
       
        public object Clone() { return null; }
    }
    class Component : ICloneable
    {
        public object Clone() { return new Component(); }
    }

 

GameObject 추상 클래스를 상속한 Pawn 클래스

class Pawn : GameObject
    {
        private Component components;

        public Pawn(string Name) : base(Name) {}
        public override void Awake() { }
        public override void Start() { }

        public override void Tick()
        {
            // if Key Is Pressed
            // ...Move
        }
        public new object Clone() 
        {
            Pawn pClone = new Pawn(base.Name);
            pClone.components = (Component)this.components.Clone();
            return pClone;
        }
    }

'C# > C# 기본' 카테고리의 다른 글

[C#] 배열 / 컬렉션 / 인덱서  (0) 2021.10.11
[C#] 프로퍼티  (0) 2021.10.08
[C#] 클래스  (0) 2021.10.06
[C#] 메서드  (0) 2021.10.05
[C#] 분기문  (0) 2021.10.02