인터페이스 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;
}
}