[ 학습 흐름 ]
[ 학습 흐름 ]
- 인터페이스의 선언
- 인터페이스를 상속하는 인터페이스
- 여러 인터페이스 . 한꺼번에 상속하기
- 추상 클래스
[ 인터페이스의 선언 ]
[ 인터페이스 ]
interface 인터페이스_이름
{
반환형식 메소드이름1(매개변수 목록);
반환형식 메소드이름2(매개변수 목록);
반환형식 메소드이름3(매개변수 목록);
반환형식 메소드이름4(매개변수 목록);
}
- 클래스와 비슷하지만 메소드,이벤트,인덱서,프로퍼티만을 가질 수 있다 . 그 마저도 구현부가 없다 .
- 인터페이스는 접근 제한자를 사용 할 수 없고 모든것이 public 으로 선언된다 .
- 인스턴스도 만들 수 없다 .
[ 인터페이스를 상속받는 클래스 ]
class ConsoleLogger:ILogger
{
public void WriteLog(string message)
{
//메서드
}
}
ILogger logger = new ConsoleLogger();
- 인터페이스를 상속받는 클래스는 인스턴스를 만들 수 있다 .
- 파생 클래스는 인터페이스에 선언된 모든 메소드를 public으로 반드시 구현해야 한다 .
- 인터페이스는 인스턴스는 못 만들지만 참조는 만들 수 있다 .
(파생 클래스가 기반 클래스와 같은 형식으로 간주되는 원리가 그대로 적용)
[ 인터페이스는 약속이다 ]
[ 인터페이스 ]
- USB 포트를 다양하게 활용할 수 있는 이유는 PC와 주변기기가 USB라는 약속을 따르기 때문이다 .
- 클래스가 따라야 하는 약속이 인터페이스이다 .
- 인터페이스는 파생될 클래스가 어떤 메소드를 구현해야 할지를 정의한다 .
using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
namespace Interface;
class MainApp
{
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
{
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
{
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);
}
}
}
static void Main(string[] args)
{
//ClimateMonitor monitor = new ClimateMonitor(new FileLogger("MyLog.txt"));
ClimateMonitor monitor = new ClimateMonitor(new ConsoleLogger());
monitor.Start();
}
}
- 예시처럼 climateMonitor 클래스를 사용하는 사용자들이 다른 방식으로 로그를 출력하려 한다면 ,
인터페이스는 좋은 해결책이 될 수 있다 .
- ClimateMonitor 클래스는 Ilogger 형식의 참조 logger를 이용해서 사용자로부터 입력받은 온도를 기록하고 ,
logger 가 어떻게 기록할지는 ClimateMonitor 생성자의 매개변수에 달려있다 .
[ 인터페이스를 상속하는 인터페이스 ]
[ 인터페이스를 상속하는 인터페이스 ]
- 인터페이스를 상속 할 수 있는건 클래스 ,구조체 ,인터페이스 .
[ 인터페이스를 인터페이스가 상속하는 이유 ]
상속하려는 인터페이스가 소스코드가 아닌 어셈블리로만 제공되는 경우
- .Net SDK에서 제공하는 인터페이스가 그 예이다 .
- 어셈블리 안에 있어 수정이 어렵기에 해당 인터페이스 새 기능을 추가하려면 상속할 수 밖에 없다 .
상속하려는 인터페이스를 상속하는 다른 클래스들이 존재할때
- 클래스는 반드시 인터페이스의 모든 메소드,프로퍼티를 구현해야 한다 .
- 기존 소스코드에 영향을 미치지 않고 새로운 기능을 추가하려면 상속하는게 좋다 .
using System;
namespace DerivedInterface;
class MainApp
{
interface ILogger
{
void WriteLog(string message);
}
interface IFormattableLogger:ILogger
{
void WriteLog(string format, params Object[] args);
}
class ConsoleLogger : 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);
}
}
static void Main(string[] args)
{
IFormattableLogger logger = new ConsoleLogger();
logger.WriteLog("The world is not flat");
logger.WriteLog("{0} + {1}= {2}", 1, 1, 2);
}
}
[ 여러 인터페이스 , 한꺼번에 상속하기 ]
[ 클래스의 다중상속 ]
죽음의 다이아몬드
- 최초의 클래스가 두개의 파생클래스로부터 상속받고 , 이 두개의 파생 클래스를 다시 하나의 클래스가 상속하는것
업캐스팅
- 모호성의 오류를 불러일으킴
위의 두 문제로 C#의 클래스는 다중 상속을 허용하지 않는다 .
[ 인터페이스의 다중상속 ]
- 인터페이스는 내용이 아닌 외형만을 강제하기에 죽음의 다이아몬드와 같은 문제가 발생하지 않음
using System;
namespace DerivedInterface;
class MainApp
{
interface IRunnable
{
void Run();
}
interface IFlyable
{
void Fly();
}
class FlyingCar:IRunnable,IFlyable
{
public void Run()
{
Console.WriteLine("Run!");
}
public void Fly()
{
Console.WriteLine("Fly!");
}
}
static void Main(string[] args)
{
FlyingCar car = new FlyingCar();
car.Run();
car.Fly();
IRunnable runnable = car as IRunnable;
runnable.Run();
IFlyable flyable=car as IFlyable;
flyable.Fly();
}
}
[ 포함 ]
MyVehicle
{
Car car = new Car();
Plane plane = new Plane();
public void Fly(){plane.Ride()}
public void Run(){car.Ride()}
}
- 구현을 물려받는 다중 상속은 포함기능을 통해 구현 할 수 있다 .
( 상속은 구현을 물려받기 위한 장치가 아닌 다형성 : 즉 다양한 버전의 모습을 가지게 하는게 목적임 )
- 클래스 안에 물려받고 싶은 기능을 가진 클래스를 필드로 선언하는 기법
[ 인터페이스의 기본 구현 메소드 ]
[ 기본 구현 메소드 ]
- 어떠한 인터페이스로부터 수없이 많은 파생클래스가 생겼다 가정했을때 이런 코드를 레거시라고 한다 .
- 레거시 코드는 업그레이드에 각별한 주의가 필요하다 . (추가시 파생클래스에도 구현하지 않으면 컴파일 에러 생기기에)
- 초기 레거시 설계시 놓친 새로운 매소드를 추가할때 기본구현 메소드를 갖도록 해서 위 문제를 해결 할 수 있다 .
- 인터페이스 참조로업캐스팅 했을때만 사용하기에 파생 클래스에서 인터페이스에 추가된 메소드 엉뚱하게 호출 할 일 X
using System;
namespace DerivedInterface;
interface ILogger
{
void WriteLog(string message);
void WriteError(string Error)
{
Console.WriteLine($"Error : {Error}");
}
}
class ConsoleLogger:ILogger
{
public void WriteLog(string message)
{
Console.WriteLine($"{DateTime.Now.ToLocalTime()},{ message}");
}
}
class MainApp
{
static void Main(string[] args)
{
ILogger logger = new ConsoleLogger();
logger.WriteLog("System Up");
logger.WriteError("Error");
ConsoleLogger clogger=new ConsoleLogger();
clogger.WriteLog("System Up");
//인터페이스에 선언된 기본 구현 인터페이스는 파생 클래스의 참조로 호출 불가
//clogger.WriteError("Error");
}
}
[ 추상클래스 ]
[ 추상 클래스 ]
- 추상 클래스는 구현을 가질 수 있지만 인스턴스는 가질 수 없다 .
- 추상 클래스는 한정자를 명시하지 않으면 모든 메소드가 private으로 선언된다
- 추상 메소드를 가질 수 있는데 , 파생 클래스의 구현을 강제한다 .
- 추상 메서드는 약속의 역할을 하기에 public,protected,internal,protected internal 중 하나로 수식될것을 강요한다 .
- 이로 인해 클래스의 접근성 원칙과 인터페이스의 접근성 원칙 모두 지킨다 .
- 또한 추상 클래스는 추상 클래스를 상속 할 수 있다 .
using System;
namespace AbstractClass;
abstract class AbstractBase
{
protected void PrivateMethodA()
{
Console.WriteLine("Base.PrivateMethodA");
}
public void PublicMethodA()
{
Console.WriteLine("Base.PublicMethodA");
}
public abstract void AbstractMethod();
}
class Derived : AbstractBase
{
public override void AbstractMethod()
{
Console.WriteLine("Derived.AbstractMethodA");
PrivateMethodA();
}
}
class MainApp
{
static void Main(string[] args)
{
AbstractBase obj = new Derived();
obj.AbstractMethod();
obj.PublicMethodA();
}
}
[ 추상 클래스의 목적 ]
- 추상 클래스는 일반 클래스가 가질 수 있는 구현과 더불어 추상 메소드를 가지고 있다 .
- 위 기능은 일반 메소드를 선언, 클래스에 대한 아래와 같은 메뉴얼을 작성해 배포해도 같은 효과를 낼것이다 .
ex)이 클래스는 직접 인스턴스 하지 말고 파생 클래스를 만들어 사용하고 , 메서드 A,B는 꼭 오버라이딩 해주세요
- 그러나 추상 클래스를 씀으로써 위의 내용은 암시적으로 다른 프로그래머에게 전달 가능하다 .
- 또한 추상 메소드의 구현 역시 컴파일러가 알려주기에 실수를 줄일 수 있다.
'C# > 이것이 C#이다' 카테고리의 다른 글
[ 이것이 C#이다 ] Chapter 10 . 배열과 컬렉션 그리고 인덱서 (0) | 2024.03.14 |
---|---|
[ 이것이 C#이다 ] Chapter 09 . 프로퍼티 (0) | 2024.03.12 |
[ 이것이 C#이다 ] Chapter 07 . 클래스 (0) | 2024.03.06 |
[ 이것이 C#이다 ] Chapter 06 . 메소드로 코드 간추리기 (0) | 2024.03.04 |
[ 이것이 C#이다 ] Chapter 05 . 코드의 흐름 제어하기 (0) | 2024.02.28 |