약속, 계약, 명세, 규격 등등
'전에 만들었던 명령 패턴처럼 기능을 추가하기 쉬운 구조가 됨'
추상클래스와 인터페이스의 차이점
상태 정보: 추상 클래스는 멤버 변수를 가질 수 있으므로 상태 정보를 저장할 수 있습니다.
구현: 추상 클래스는 일부 메서드가 구현될 수 있습니다. 즉, '메서드에 대한 기본 구현을 제공'할 수 있습니다.
접근 지정자: 추상 클래스의 메서드는 public, protected, internal 등 다양한 접근 지정자를 가질 수 있습니다.
상속: '추상 클래스를 상속받는 클래스는 반드시 추상 클래스의 모든 추상 메서드를 구현'해야 합니다. 추상 클래스는 단일 상속만 지원합니다.
생성자: 추상 클래스에는 생성자가 있을 수 있으며, 이를 통해 상속받는 클래스에서 반드시 초기화해야 하는 로직을 정의할 수 있습니다.
상태 정보: 인터페이스는 '멤버 변수를 가질 수 없으'므로 상태 정보를 저장할 수 없습니다.
구현: 인터페이스의 모든 메서드는 추상 메서드입니다. 즉, '메서드에 대한 구현을 가질 수 없고' 오로지 '선언만 가능'합니다. // 재정의 강제
접근 지정자: 인터페이스의 모든 메서드는 암시적으로 public 입니다.
상속: 인터페이스를 구현하는 클래스 또는 인터페이스는 인터페이스의 모든 메서드를 구현해야 합니다. 인터페이스는 '다중 상속을 지원'합니다.
생성자: 인터페이스에는 '생성자가 없습니다.'
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public interface IEnumerator
{
bool MoveNext();
object Current { get; } // 얘는 모양이 오토프로퍼티지만 오토프로퍼티가 아니라 선언만 써논 거임
void Reset();
}
internal class Countdown : IEnumerator
{
int count = 11;
public bool MoveNext() => count-- > 0;
public object Current => count;
public void Reset() { throw new NotSupportedException(); }
}
IEnumerator e = new Countdown();
while (e.MoveNext())
Console.Write (e.Current); // 1098765
인터페이스 상속받은 후 재구현 강제
서로 반드시 갖고 있어야할 메서드 정의(?)
'인터페이스는 override를 붙이지 않음'
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public interface IUndoable { void Undo(); }
public class TextBox : IUndoable
{
public virtual void Undo() => Console.WriteLine ("TextBox.Undo");
}
public class RichTextBox : TextBox
{
public override void Undo() => Console.WriteLine ("RichTextBox.Undo");
}
RichTextBox r = new RichTextBox();
r.Undo(); // RichTextBox.Undo
((IUndoable)r).Undo(); // RichTextBox.Undo
((TextBox)r).Undo(); // RichTextBox.Undo
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
C#에서 인터페이스의 메서드를 구현할 때 발생할 수 있는 이름 충돌을 해결하는 메커니즘이다.
명시적으로 인터페이스를 구현하면 해당 메서드는 클래스 레벨에서는 직접 호출할 수 없으며, 인터페이스 타입을 통해서만 접근 가능하다.
이름이 같은 메서드를 가진 두 개 이상의 인터페이스를 구현하려면 충돌이 발생할 수 있다.
예를 들어, 위의 I1과 I2 인터페이스는 모두 Foo()라는 메서드를 가지고 있다.
Widget 클래스가 이 두 인터페이스를 모두 구현하려면 이 충돌을 어떻게 해결할지 결정해야 한다.
Widget 클래스에서 I1의 Foo()는 일반적으로 구현하고(public void Foo()),
I2의 Foo()는 명시적으로 구현(int I2.Foo())한다. 이렇게 하면, Widget 클래스의 인스턴스를 통해
Foo()를 호출하면 I1의 구현이 실행된다. I2의 Foo()를 호출하려면 I2로 형변환한 후에 호출해야 한다.
명시적으로 구현된 인터페이스 멤버는 해당 인터페이스 타입으로 형 변환된 객체를 통해서만 접근할 수 있다.
예를 들어, ((I2)w).Foo()처럼 Widget 객체 w를 I2로 형변환해야 I2.Foo()를 호출할 수 있다.
명시적 인터페이스 구현은 인터페이스 구현의 이름 충돌을 피하는 유용한 방법이다.
하지만 명시적으로 구현된 멤버는 해당 인터페이스로 형변환하여 접근해야 한다는 제약이 있다.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
인터페이스 멤버를 가상 함수로 재정의 가능함. 기본값은 sealed
public interface IUndoable { void Undo(); }
public class TextBox : IUndoable
{
public virtual void Undo() => Console.WriteLine ("TextBox.Undo");
}
public class RichTextBox : TextBox
{
public override void Undo() => Console.WriteLine ("RichTextBox.Undo");
}
RichTextBox r = new RichTextBox();
r.Undo(); // RichTextBox.Undo
((IUndoable)r).Undo(); // RichTextBox.Undo
((TextBox)r).Undo(); // RichTextBox.Undo
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public interface IUndoable { void Undo(); }
public class TextBox : IUndoable
{
void IUndoable.Undo() => Console.WriteLine ("TextBox.Undo"); // explicitly, virtual
}
public class RichTextBox : TextBox, IUndoable
{
public void Undo() => Console.WriteLine ("RichTextBox.Undo");
}
RichTextBox r = new RichTextBox();
r.Undo(); // RichTextBox.Undo Case 1
((IUndoable)r).Undo(); // RichTextBox.Undo Case 2
public class TextBox : IUndoable
{
public void Undo() => Console.WriteLine ("TextBox.Undo"); // implicitly, break
}
RichTextBox r = new RichTextBox();
r.Undo(); // RichTextBox.Undo Case 1
((IUndoable)r).Undo(); // RichTextBox.Undo Case 2
((TextBox)r).Undo(); // TextBox.Undo Case 3 // break
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public class TextBox : IUndoable
{
void IUndoable.Undo() => Undo(); // Calls method below
protected virtual void Undo() => Console.WriteLine ("TextBox.Undo");
}
public class RichTextBox : TextBox
{
protected override void Undo() => Console.WriteLine("RichTextBox.Undo");
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
interface I { void Foo(); }
struct S : I { public void Foo() {} } // 구조체는 인터페이스를 상속받을 수 있다
...
S s = new S();
s.Foo(); // 박싱 X
I i = s; // 인터페이스에 형 변환할 때 상자가 발생합니다.
i.Foo();
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
interface ILogger
{
void Log (string text) => Console.WriteLine (text);
}
class Logger : ILogger { }
(new Logger()).Log ("message"); // compile error
((ILogger)new Logger()).Log ("message");
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
abstract class Animal {}
abstract class Bird : Animal {}
abstract class Insect : Animal {}
abstract class FlyingCreature : Animal {} // interface IFlyingCreature {}
abstract class Carnivore : Animal {} // interface ICarnivore {}
// Concrete classes:
class Ostrich : Bird {}
class Eagle : Bird, FlyingCreature, Carnivore {} // Illegal
class Bee : Insect, FlyingCreature {} // Illegal
class Flea : Insect, Carnivore {} // Illegal
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////