interface
키워드 이용I
로 시작하는 이름으로 명명public
으로 선언됨예제 프로그램
// 인터페이스
interface ILogger
{
void WriteLog(string message);
}
// 파생 클래스
class ConsoleLogger : ILogger // 상속
{
public void WriteLog(string message) // WriteLog() 메소드 구현 & public 한정자 수식 강제
{
Console.WriteLine(
"{0} {1}",
DateTime.Now.ToLocalTime(), message);
}
}
// 파생 클래스의 인스턴스화
ILogger logger = new ConsoleLogger();
logger.WriteLog("Hello, World!");
public
한정자로 수식해야 한다.기반 클래스(부모) ≒ 파생 클래스(자식)
이 성립되는 것예제 프로그램 -
콘솔에 로그 출력
using System;
using System.IO;
namespace Interface
{
// 기반 클래스 : 인터페이스
interface ILogger
{
void WriteLog(string message);
}
// 파생 클래스1
class ConsoleLogger : ILogger
{
public void WriteLog(string message) // 약속 : WriteLog(), public 한정자 구현
{
Console.WriteLine(
"{0} {1}",
DateTime.Now.ToLocalTime(), message);
}
}
class ClimateMonitor
{
private ILogger logger; // ILogger 참조를 필드로 지님
public ClimateMonitor(ILogger logger) // 필드 통해 초기화
{
this.logger = logger;
}
public void start()
{
while (true)
{
Console.Write("온도를 입력해주세요. : ");
string temperature = Console.ReadLine(); // 온도 입력받기
if (temperature == "")
break;
logger.WriteLog("현재 온도 : " + temperature);
}
}
}
class MainApp
{
static void Main(string[] args)
{
// ConsoleLogger 객체를 생성자 인수로 넘겨 "콘솔에 메시지 출력"
ClimateMonitor monitor = new ClimateMonitor(new ConsoleLogger());
monitor.start();
}
}
}
예제 프로그램 -
텍스트파일에 로그 출력
using System;
using System.IO;
// 기반 프로그램 : 인터페이스
namespace Interface
{
interface ILogger
{
void WriteLog(string message);
}
// 파생 클래스1
class ConsoleLogger : ILogger
{
public void WriteLog(string message)
{
Console.WriteLine(
"{0} {1}",
DateTime.Now.ToLocalTime(), message);
}
}
// 파생 클래스2
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; // ILogger 참조를 필드로 지님
public ClimateMonitor(ILogger logger) // 필드 통해 초기화
{
this.logger = logger;
}
public void start()
{
while (true)
{
Console.Write("온도를 입력해주세요. : ");
string temperature = Console.ReadLine(); // 온도 입력받기
if (temperature == "")
break;
logger.WriteLog("현재 온도 : " + temperature);
}
}
}
class MainApp
{
static void Main(string[] args)
{
// FileLogger 객체를 생성자 인수로 넘겨 "파일에 로그 기록"
ClimateMonitor monitor = new ClimateMonitor(new FileLogger("MyLog.txt"));
monitor.start();
}
}
}
txt파일
한글깨짐 없이 여는 법type
명령어 통해 파일 읽기949
에서 한글이 깨지는 문제가 발생하므로 유니코드 65001
설정예제 프로그램 -
상속을 통한 class connect 역할
using System;
using System.IO;
namespace Interface
{
interface ILogger
{
void WriteLog(string message);
}
/* ConsoleLogger와 FileLogger는 ILogger를 상속하며, WriteLog()메소드를 구현함으로써
기반클래스(부모) ≒ 파생클래스(자식)로 간주되어
ILogger 인터페이스를 통해 다른 클래스(ClimateMonitor)와 연결되어 소통한다. */
// 파생 클래스1
class ConsoleLogger : ILogger
{
public void WriteLog(string message)
{
Console.WriteLine(
"{0} {1}",
DateTime.Now.ToLocalTime(), message);
}
}
// 파생 클래스2
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.Write("온도를 입력해주세요. : ");
string temperature = Console.ReadLine();
if (temperature == "")
break;
logger.WriteLog("현재 온도 : " + temperature);
}
}
}
class MainApp
{
static void Main(string[] args)
{
/* [ class connect ]
monitor 객체는 인터페이스를 통해 연결된 FileLogger를 통해
애플리케이션이 시작된 디렉터리에 MyLog.txt 파일을 만들고 여기에 로그를 남긴다. */
ClimateMonitor monitor = new ClimateMonitor(new FileLogger("MyLog.txt"));
monitor.start();
}
}
}
[비타민 퀴즈]
ClimateMonitor의 logger가 ConsoleLogger의 객체를 가리킬 경우 실행 결과ClimateMonitor monitor = new ClimateMonitor(new ConsoleLogger());
interface 파생 인터페이스 : 부모 인터페이스
{
// ... 추가할 메소드 목록
}
파생 인터페이스는 기반 인터페이스에 선언된 모든 것을 그대로 물려받는다는 점을 잊지 말것
예제 프로그램
using System;
namespace DerivedInterface
{
// 기반 인터페이스
interface ILogger
{
void WriteLog(string message);
}
// 상속1 : 파생 인터페이스
interface IFormattableLogger : ILogger
{
void WriteLog(string format, params Object[] args);
}
// 상속2 : 파생 클래스
class ConsoleLogger2 : IFormattableLogger
{
public void WriteLog(string message)
{
Console.WriteLine(
$"{DateTime.Now.ToLocalTime()}, {message}");
}
public void WriteLog(string format, params Object[] args)
{
String message = String.Format(format, args);
Console.WriteLine(
$"{DateTime.Now.ToLocalTime()}, {message}");
}
}
// 실행
class MainApp
{
static void Main(string[] args)
{
IFormattableLogger logger = new ConsoleLogger2();
logger.WriteLog("The world is not flat.");
logger.WriteLog("{0} + {1} = {2}", 1, 1, 2);
}
}
}
예제 프로그램
using System;
namespace MultiInterfaceInheritance
{
// 인터페이스1
interface IRunnable
{
void Run();
}
// 인터페이스2
interface IFlyable
{
void Fly();
}
// 인터페이스 다중 상속 클래스
class FlyingCar : IRunnable, IFlyable
{
public void Run()
{
Console.WriteLine("Run! Run!");
}
public void Fly()
{
Console.WriteLine("Fly! Fly!");
}
}
class MainApp
{
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();
}
}
}
그래도 여러 클래스로부터 구현을 물려받고 싶다면?
- 포함(Containment)기법
: 클래스 안에 물려받고 싶은 기능을 가진 클래스들을 필드로 선언해 넣는 것MyVehicle() { Car car = new Car(); Plane plane = new Plane(); public void Fly() { plane.Ride(); } public void Run() { car.Ride(); } }
기본 구현 메소드 : 구현부를 가지는 메소드
// ILogger의 모든 파생 클래스에 대해 컴파일 에러를 발생시키는 경우 //
indterface ILogger
{
void WriteLog(string message);
void WriteError(string error); // 새로운 메소드 추가 : 컴파일 에러 발생
}
// 컴파일 에러 막는 법 : 기본 구현 메소드 //
indterface ILogger
{
void WriteLog(string message);
void WriteError(string error) // 다른 기존 코드에 아무런 영향 끼치지 않음
{
WriteLog($"Error : {error}"); // WriteError()에 기본 구현 제공
}
}
예제 프로그램
using System;
namespace DefaultImplementation
{
// 기반 인터페이스
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 MainApp
{
static void Main(string[] args)
{
ILogger logger = new ConsoleLogger();
logger.WriteLog("System Up"); // OK
logger.WriteError("System Fail"); // 기본 구현 메소드로 인해 OK
ConsoleLogger clogger = new ConsoleLogger();
clogger.WriteLog("System Up"); // OK
// clogger.WriteError("System Fail");
// 컴파일 에러 : ConsoleLogger가 Writerror() 오버라이딩X
}
}
}
메소드의 구현 가질 수 있음(=클래스)
객체 생성 불가(=인터페이스)
인터페이스를 제공하되 기본적인 구현을 함께 제공하고 싶을 경우 사용
추상 클래스는 또 다른 추상 클래스를 상속할 수 있다.
사용 목적 : 내가 만든 추상 클래스를 이용할 때 이에 대한 규칙/약속 강제
선언
// 문법 //
abstract class 클래스이름
{
// 클래스와 동일하게 구현
}
// 추상 메소드 선언 예 //
abstract class AbstractBase // 추상 클래스
{
public abstract void SomeMethod(); // 추상 메소드 : abstract 한정자 이용
}
class Derived : AbstractBase
{
public override void SomeMethod()
{
// Something
}
}
예제 프로그램
using System;
namespace AbstractClass
{
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 MainApp
{
static void Main(string[] args)
{
AbstractBase obj = new Derived();
obj.AbstractMethodA();
obj.PublicMethodA();
}
}
}
인터페이스와 클래스가 다른 점은 무엇입니까?
: 인터페이스는 클래스와 달리 내부에 메서드(함수)와 프로퍼티, 인덱서만 선언 가능하며, new 키워드를 통해 인스턴스화 할 수 없다. 또한 접근 지정자가 기본적으로 public
으로 설정되어 있다. 따라서 해당 인터페이스를 상속받는 클래스를 만들어, 업 캐스팅 형식의 참조를 통해 인터페이스를 활용해야 한다.
인터페이스는 하나의 약속이다. 인터페이스를 상속받는 모든 파생 클래스들은 인터페이스에 선언되어 있는 함수를 무조건 정의해야 한다. 따라서 어떤 프로그래머가 해당 클래스가 어떤 인터페이스를 상속받고 있는지만 알고 있어도, 해당 클래스가 어떤 기능을 하는지 유추할 수 있다.
인터페이스와 추상 클래스가 다른 점은 무엇입니까?
: 추상클래스는 인터페이스와 기본적으로 역할(함수의 정의를 강요)은 비슷하지만, 인터페이스와 달리 데이터(변수)를 선언할 수 있다. 또한 필요에 의해 함수를 정의해도 괜찮다. 추상 클래스에는 해당 클래스에서만 사용 가능한 추상 메서드라는 것이 존재하는데, 이는 인터페이스의 메서드와 같은 역할을 한다. 하지만 모든 추상 클래스 내의 메소드와 데이터는 private으로 설정되어 있기 때문에, 추상 메서드의 접근 지정자를 public으로 설정하는 것을 추천한다.