C#.

이것이 C#이다 8장) 인터페이스와 추상클래스

Muru 2023. 8. 25. 00:29

인터페이스와 추상클래스 

C#에서 인터페이스란 무엇일까?

추상화를 통해 다양한 클래스가 동일한 동작을공유하도록 하는 개념을 Interface라 한다. 이 인터페이스는 메서드, 속성, 이벤트 및 인덱서가 정의 가능한데, 구현할때 멤버들을 반드시 구현해야한다.

인터페이스는 클래스간의 결합도를 낮추고 유연한 설계를 할수있게 해준다. 실무에서 보통 대게 인터페이스 이름 앞에 'I'를 붙여준다.

 

다음은 온도를 통한 예제이다.

using System;
using System.IO;

namespace Interface
{
    interface ILogger
    {
        void WriteLog(string message);
    }
    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.ToShortTimeString(), message);
        }
    }

    class ClimateMonitor
    {
        private ILogger logger;
        public ClimateMonitor(ILogger logger)
        {
            this.logger = logger;
        }

        public void start()
        {
            while (true)
            {
                Console.WriteLine("온도를 입력해주세요 : ");
                string temperature = Console.ReadLine();
                if (temperature == "")
                    break;

                logger.WriteLog("현재 온도 : " + temperature);
            }
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
           ClimateMonitor monitor = new ClimateMonitor(new FileLogger("MyLog.txt"));   //monitor 객체는 어플리케이션이 시작된 디렉터리에 MyLog.txt를 만들고 여기에 로그를 남깁니다.

            monitor.start();
        }
    }
}

//

실행결과 
온도를 입력해주세요 : 22
온도를 입력해주세요 : 30
온도를 입력해주세요 : 26

MyLog.txt내용
오후 11:31 현재 온도 : 22
오후 11:31 현재 온도 : 30
오후 11:31 현재 온도 : 26

 

 


인터페이스를 상속하는 인터페이스

인터페이스 위에 인터페이스를... 얹어드시겠습니까? Interface위에 새로운 Interface를 상속해봅시다.

using System;

namespace DerivedInterface
{

    interface ILogger
    {
        void WriteLog(string message);
    }

    interface IFormattableLogger : ILogger
    {
        void WriteLog(string format, params Object[] args);
        //IFormattableLogger는
        //Void WriteLog와 Void WriteLog(string format, params Object[] args) 메소드 두 개를 가지게된다.
    }

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

        public void WriteLog(string format, params object[] args)
        {
            String message = String.Format(format, args);
            Console.WriteLine("{0} {1}", DateTime.Now.ToLocalTime(), message);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            IFormattableLogger logger = new ConsoleLogger2();
            logger.WriteLog("The world is not flat.");
            logger.WriteLog("{0} + {1} = {2}", 1, 1, 2);


        }
    }
}


실행결과
2023-08-24 오후 11:42:02 The world is not flat.
2023-08-24 오후 11:42:02 1 + 1 = 2

 

인터페이스 한꺼번에 상속하는 법

 

인터페이스는 일단 한꺼번에 상속시키지 않는다. '죽음의 다이아몬드'라는 문제때문. 무엇을 출력할지 모르기때문에, C#은 다중 상속을 허용하지 않는다.

 

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

 

인터페이스에 새로운 메서드를 추가했는데, 오버라이딩 안할시 출력되는 것.

using System;

namespace ConsoleApp10
{
    interface ILogger
    {
        void WriteLog(string message);

        void WriteError(string error) //새 메서드 추가
        {
            WriteLog($"Error: {error}");
        }
    }

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

    class Program
    {
        static void Main(string[] args)
        {
            ILogger logger = new ConsoleLogger();
            logger.WriteLog("System Up~!");
            logger.WriteError("System Fail...");

            ConsoleLogger clogger = new ConsoleLogger();
            clogger.WriteLog("System Up~?");    //Ok
        }
    }
}

실행결과
2023-08-24 오후 11:58:21, System Up~!
2023-08-24 오후 11:58:21, Error: System Fail...
2023-08-24 오후 11:58:21, System Up~!

추상클래스(abstract)

 

추상클래스는 '구현'을 가질수있다. 하지만 클래스와 달리 인스턴스는 가질 수 없다. 접근성을 이야기해보자면 인터페이스에 모든 메소드는 public이지만, 클래스는 한정자를 명시하지 않으면 모든 메소드가 private로 선언된다..

 

추상 클래스는 클래스와 다른점이 추상 메소드(Abstract Method)를 가질 수 있다. 이 추상 메소드는 추상 클래스가 인터페이스의 역할도 할 수 있게 해주는 장치다. '구현'은 못 가지지만, 파생클래스에서 반드시 갖도록 강제를 하기에... 그렇다면 이 메서드의 접근성은 어떨까? 이 메서드들은 기본이 private인 추상 클래스와 다르게 메서드는 public, protected, internal, protected internal 한정자 중 하나로 수식될 것을 C# 컴파일러가 강요한다. 

 

다음 예제를 보자.

using System;

namespace ConsoleApp10
{
   abstract class AbstractBase
    {
        protected void PrivateMethodA() => Console.WriteLine("AbstractBase.PrivateMethodA()");

        public void PublicMethodA() => Console.WriteLine("AbstractBase.PublicMethodA()");

        public abstract void AbstractMethodA();

    }

    class Derived : AbstractBase
    {
        public override void AbstractMethodA()
        {
            Console.WriteLine("Derived.AbstractMethodA()");
            PrivateMethodA();
        }
    }           
    class Program
    {
        static void Main(string[] args)
        {
            AbstractBase obj = new Derived();
            obj.AbstractMethodA();
            obj.PublicMethodA();
        
        }
    }
}

    
   실행결과
   Derived.AbstractMethodA()
   AbstractBase.PrivateMethodA()
   AbstractBase.PublicMethodA()

Derived.AbstractMethodA()

Dervied 클래스는 AbstractBase 클래스를 상속 받고, AbstractMethodA() 를 Dervied 클래스에 구현중이므로

obj.AbstractMethodA() 를 실행시 Derived.AbstractMethodA()가 출력된다.

 

AbstractBase.PrivateMethodA()

PrivateMethodA()를 출력하고싶어도 (private) abstract class AbstractBase 이므로 못하지만,

Dervied 클래스 내부에서는 PrivateMethodA()이 접근 가능하므로 AbstractBase.PrivateMethodA()가 출력된다.

 

AbstractBase.PublicMethodA()

AbstractBase 클래스의 PublicMethodA()는 public이므로.

 


추상클래스는 일반 클래스가 가질 수 있는 구현과 더불어 추상메소드를 가질수있다. 한마디로 추상 클래스의 인스턴스를 다루는 것과 코드의 중복을 피하고 유지 보수성을 향상시키는 기능 덕분에 프로그램의 유연성과 확장성이 높인다..라나 뭐라나..