- 프롤로그
- Boilerplate code(행사코드) : 해결하고자 하는 문제의 해결책과 상관이 없는 코드, Ceremony 라고 앤더슨 하일스버그가 표현.
- 모든 언어의 '발전'은 추상수준을 상승시켜서 프로그래머가 작성해야 하는 행사코드의 분량을 줄이는 방향으로 움직인다. =>Infra쪽에서 같은 방향으로 ? AWS에서 infra service를 제공하며 개발자에게 Business에 더 집중하라는 것과 같은 맥락인가.
- 자바가 전성기를 구가하고 있다고 여기는 사람은 이제 없다. 자바는 더이상 핫하지 않다.
- 프로그래머가 어떤 언어에 대해서 얼마나 많이 알고 있는가 하는 것이 아니라 어떤 언어를 얼마나 빠르게 학습할 수 있는가 하는 것이 더 중요하다. => 그러면 그 언어가 왜 만들어졌으며 특성을 이해하고 사용하는 것도 중요하겠지.
- c#과 파이썬을 능숙하게 다룰 수 있다면 그의 폴리글랏 등급은 비로소 '상급' 수준이다. => 이렇게 공부하고 있다. ^^
- 함수형 언어는 변수가 야기하는 부수 효과를 제거하기 때문에 멀티쓰레드 코드를 작성하는 것이 더 용이하다. => 함수형 프로그래밍은 개발자가 병렬처리를 하는 코드를 작성하기보다 실행환경에 병렬처리를 하도록 프로그래밍하므로
- 자바
- 자바를 넘어서
- 자바에 비해서 더 동적 언어들은 AOP나 의존성 주입 패턴을 거의 사용하지 않는다. 이것은 자바에서는 매우 중요한 문제를 해결할 때 사용되지만 , 동적언어들은 자바가 프로그래머에게 강제하는 고통을 요구하지 않는다. => 동적언어를 배우자
- 동적 프로그래밍의 단점은 코드를 작성하는 동안에 컴파일러의 도움을 얻을 수 없다. 실행하는 동안에 더 많은 버그를 만날 수밖에 없음 => 그러므로 TDD 기반의 프로그래밍이 더 요구되는 것 아닐까? /
- 다른 단점은 매서드의 목록이 자동적으로 나타나는 intellisence 기능도 쓸 수없고 리펙토링과 관련한 기능도 거의 사용할 수없다. 루비와 같은 동적 언어를 이용해서 엔터프라이즈 수준의 프로젝트를 진행해본 사람이라면 동적 프로그래밍언어의 한계를 알 것이다.
- 동적 프로그래밍 언어가 정적프로그래밍 언어에 비해서 생산성이 높다는 주장은 비교적 규모가 작은 프로젝트에서는 사실일 가능성이 높지만 , 규모가 큰 프로젝트에서는 반드시 사실이라고 말히기 어렵다.
- 람다와 클로저
- 자신을 둘러싸고 있는 외부의 코드에서 선언한 변수를 마음대로 접근할 수 있는 코드
- 익명 클래스가 바로 클로저는 아니다. 외부에서 선언한 자유변수가 필요하다.
- 익명클래스의 내용 혹은 ActionPerformed 메서드 내부 코드가 Foo()가 실행될 때 곧바로 실행되는 것이 아니라 , 사용자가 버튼을 누를 때 실행. 이벤트 발생 시점에 실행 , 게으른 행동 , Lazy
- 자바는 외부 변수에 final n = 1 이라고 선언하여 / n은 변경 가능한 자유변수가 아니다. / 클로저를 제대로 지원하는 C#과 루비는 변경 가능하다. 그러므로 람다를 이용한 간결하고 풍부한 표현이 가능하다.
- 람다는 익명클래스보다 더 간단하고 범위가 작은 익명 메서드라고 말할 수 있다.
- 람다는 코드조각 혹은 표현을 다른 겍체나 메서드에 전달하기 위한 표현식 / ( ) -> system.out.printLn("Clicked!)
- 클로저는 외부에서 정의된 변수를 참조하는 코드조각 혹은 표현을 의미한다. ( ) -> system.out.printLn("Clicked! n = " + n) //n은 외부에서 정의
- 변경 불가능성에 대한 5가지 규칙 / 멀티쓰레딩 환경에서 동시성 문제를 발생시키지 않고 안전하게 사용
- 객체의 상태를 변경시키는 메서드를 제공하지 마라
- 클래스가 상속되지 못하도록 final 로 만들어라
- 모든 필드를 final로 선언하라
- 모든 필드를 private로 선언하라
- 변경불가능성을 만족시키지 못하는 컴포넌트에 대한 접근을 통제하라.
- 상위 수준의 언어가 성취하는 것은 무엇인가? 그것은 비본질적인 복잡성으로부터 프로그램을 자유롭게 만드는 것이다. 쉽게 말하면 프로그래머는 시시콜콜하고 세세한 대상을 반복해서 다루는 대신, 더 크고 개략적인 추상적인 개념을 가지고 주어진 문제를 해결하는데 집중해야 한다는 말이다.
- 연산자 오버로딩
- 새로운 언어의 배경이 되는 언어는 행사코드가 범람하게 되면서 생산성이 점점 낮아진다. 새로 탄생하는 언어는 배경이 되는 언어의 추상수준을 한 단계 상승시킨 모습으로 새롭게 담장을 하고 나타나서 새 시대의 출발을 선언한다.
- 좋은 프로그래머는 다른 사람이 사용할 수 있는 어휘를 늘려 나간다. 다시 말해서 그들은 자기도 모르는 사이에 언어를 설계하는 것이다. 언어의 설계를 처음부터 시작하는 것은 아니지만 기초가 되는 언어의 프레임 안에서 새로운 언어를 만들어 나간다.
- 성장이 가능한 언어를 만드는 것이다.
- 자바는 연산사자 오버로딩 기능을 지원하지 않는 유일한 언어
- 제네릭
- 제네릭이라는 기능을 정확하게 사용하지 못하거나 남용하는 프로그래머를 보면 사용하지 말라고 권한다. 스타일은 조금 떨어져도 정확한 코드를 작성하는 것이 언제나 우선이다.
- 어떤 타입 S(Number)와 T(Integer)사이에 상위클래스-하위클래스 관계가 있다고 해서 Collection<S> , Collection <T>라는 서로 다른 타입 사이에서도 똑같은 관계가 자동적으로 성립하지 않는다. 하지만 프로그래밍 언어가 일정한 노력을 기울려서 성립하도록 만들어 줄수 있다. 성립하면 공변을 지원한다고 하는 것이다.
- 자바는 배열은 공변이지만 제네릭에서는 공변이 성립하지 않는다.
- case 1 . boolean addAll(Colleciton<? extend Number> c) / extend를 사용해서 트릭사용
- case 2 . boolean addall(Collection<Number> c) / numbers.addAll(integers) comfile Error
- 반공변 : List<Number>가 기대되는 자리에 list<Integer>를 집어넣을 수 있는 경우 , 다시 말해 List<하위타입>이 기대되는 자리에 List<상위타입>을 집어 넣는 것이다.
- C#의 제네릭은 자바의 제네릭에 비해서 실행시간 성능이 좋다는 이점과 , 코드를 더 간결하게 작성할 수 있다는 장점
Public void Add<E>(E fruit)
{
if (fruit.GetType() == typeof(Apple))
Console.Out.WriteLine("I got an apple");
else if (fruit.GetType() == typeof(List<Apple>)) 자바로 작성시 에러 / 아래 코드로 작성
Console.Out.WriteLine("I got a list of apples")
else if (fruit.GetType() == typeof(List<?>)) // 타입 검사 기능을 하지 못함/ 배나 귤을 넣어도 apple을 출력 @.@
Console.Out.WriteLine("I got a list of apples")
}
- 람다는 C# 1.0 시절에 이미 대리자(delegate) 형태로 , C# 3.0에서는 깔끔한 람다 형태로 지원되어 왔다.
- 크로스 커팅 : 어떤 기능이 애플리케이션 전체에 걸처서 고루 작용한다는 뜻 , 예를 들어 로깅 메시지 지원
- C#과 같은 언어에서 클로저가 변경가능한 mutable 값을 참조하기 때문에 일어나는 골치 아픈 문제.
- 아래 링크에 자세한 설명
- https://yuddomack.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%81%B4%EB%A1%9C%EC%A0%80Closure
List<Action> actions = new List,Action>();
for(int i = 0; i < 10 ; i++)
{
actions.Add( () => Console.WriteLine(i) ); // 클로저, i라는 자유 변수를 참조
}
for each (Action action in actions) action();
결과는 모두 10을 출력한다. for 문이 끝난 후에 지역 변수 i는 사라져야 하지만 closure에서는 자유변수 i는 10으로 값을 유지 한다.
for(int i = 0; i < 10 ; i++)
{
int temp_var = i; // Loop의 내부에 변수를 선언해서 클로저에 넘기면
actions.Add( () => Console.WriteLine(temp_var) );
}
for each (Action action in actions) action();
결과는 1,2,3,.....10을 출력한다. for문이 끝난 후에 자유변수 temp_var을 각각 1,2,3...10으로 closure안에 유지되어 있는 것으로 생각된다.
- C#
- 델파이
- 델파이와 C#를 만든 사람이 같다. 닉켈스 워스(Nickel's worth) / 닉 라우스 비어트
- 어떤 표현의 대한 실행 혹은 평가를 미래의 어느 시점으로 연기하는 방식으로 전달하는 것이 바로 Call by Name
- 이름에 의한 전달은 람다를 통해서 할 수 있는 여러가지 유용한 작업의 하나이다.
- 속성과 대리자
Java & C#
public class Person {
private String name;
public String getName() { return name; }
public void setName(String name) { thi.name = name; }
}
person.name
person.name = "Obama" // 이렇게 쓰고 싶으면 name을 public으로 정의해야 함 => 객체지향 캡슐와를 반하는 Antipattern
-----------------------------------------------------------------------------------------------------------------------------------------------------
C# 1.0
public class Person {
private String name ; {
public String Name
get { return name; }
set { name = value; }
}
}
C# 3.0
public class Person {
public String Name { get ; set ; }
}
person.name = "Obama" 으로 캡슐화를 위반하지 않고 사용할 수 있다. 맞아???
책이 이상한 듯 아니면 내가 이해를 못했다. 위와 같이 하면 외부에서 변경이 가능하다. 캡슐화 위반
아래와 같이 private 키워드를 붙여야 캡슐화가 가능하다. 하지만 person.name = "Obama"는 내부에서만 사용가능
public class PersonA
{
private String name;
public String Name
{
get { return name; }
private set { name = value; }
}
}
public class Person
{
public string Name { get; private set; }
}
- 일급함수
- 고계함수 : 어떤 함수에게 함수를 인수로 전달한다고 했을 때, 다른 함수를 인수로 받아들이는 함수 , Higher-order
ex ) 1,2,3,4,5 를 입력하면101, 102 ~105를 출력하는 함수를 작성
- Java - 명령형의 코드
Collection<Integer> input = Arrary.asList(1,2,3,4,5);
List<Integer> output = new ArryList,Integer>();
for (int n : input) {
output.add(n + 100) ;
}
system.out.printLn(output);
- C# - 선언적 코드
var input = Enumarable.Range(1,5);
var output = input.Select(i => i + 100); // Select는 i => i + 100을 받아들이는 고계함수
- 명령적 접근과 선언적 접근이 갖는 차이는 엄청나다. 만약 위의 예에서 5천만 개의 정수를 처리한다고 하자. 그럼 병렬젹으로 처리하기 알맞다.
- 명령형으로 작성한 코드에서는 병졀적 처리 과정을 프로그래머 자신이 직접 구현해야 한다.
- 그러한 동시성 코드를 아무런 버그가 없이 작성하는 것이 어렵고, 멀티코어를 최대한 효율적으로 사용하는 코드를 작성하기도 쉽지 않다. 하지만 이러한 코드가 고계함수를 이용해서 작성되었다면 JVM이나 CLR과 같은 실형환경 자체가 병렬적 처리를 구현하기에 더 용이하다. C#에서는 PLINQ이름으로 구현되어 있다.
- 부수효과
public inf f(int n)
{ return n + 1; }
f 함수를 여러번 call을 해도 언제나 항상 리턴 값은 n + 1로 동일하다. 부수효과가 없다.
public inf g(Person p)
{
p.Age += 1;
return p.Age;
}
g함수를 여러번 call하면 1, 2, 3 함수를 호출할 때마다 값이 1씩 증가한다. 부수요과가 있다.
- 함수형 프로그래밍 패러다임은 함수와 값의 등가성이라는 물리학 법칙을 기초로 해서 g와 같은 함수는 토대가 무너진다.
- cf) 데이타 베이스 접근, 네트워크 통신, 메모리값 변경, GUI 화면의 출력, 로그 파일 등은 함수형 프로그래밍 관점에서는 부수효과지만 프로그래밍에서 필요하다.
- 부수효과가 발생하는 매서드를 작성한 코드는 버그가 숨어서 살아갈 수 있는 음습한 그늘을 제공하기 때문에 나쁘다.
- 부수효과가 없는 프로그래밍, 변경불가능성(Immutable)을 최대한 활용한 프로그래밍을 가능하게 한다.
- 람다표현에서 부수효과를 갖는 코드를 작성하는 사람은 람다를 사용하는 이유를 제대로 이해하지 못한 사람일 가능성
- 또다른 차이점
- Try-finally와 try-catch가 거의 10대 1의 비율로 사용되어야 한다.
- 예외를 다루는 장소는 하나의 장소로 집중되어야 한다. 프로그램 안에 있는 100갸의 장소에서 가각 저마다의 예외를 다루면서 화면에 에러창을 나타낸다고 생각해봅시다. 만약 에러창을 구성하는 방식을 다르게 만들기로 결정했다면 정말 끔찍하다.
Public void a() {
try {
b();
} catch (Exeception ex) {
//handle exception
}
}
Public void b() {
try {
c();
} finally {
// clean resource
}
}
Public void c() {
try {
//work
} finally {
// clean resource
}
}
- 하일스 버그는 대부분의 코드는 finally를 이용해서 스스로를 보호하고, 콜스택의 최상위 부분에서 예외를 잡아서 처리하면 충분하다고 주장한다.
- 하지만 고스링은 보수적이고 신중한 곳, 프로그램의 실패를 대단히 심각하게 여기는 곳에서는 많은 곳에서 예외를 발생시켜 소프트웨어의 실패를 최대한 빠르게 다루어야 한다는 주장을 한다. 이것도 맞는 이야기 같다.
- LINQ
- 데이타를 읽는 질의문query을 전보다 선언적인 방식으로 작성하는 것을 가능
- 서로 다른 도메인의 데이터를 애플리케이션 내부에서 일관성 있는 방식으로 접근할 수 있도록 해주는 것은 상당히 의미가 있다.
- 마치 프로그래밍 언어와 데이터베이스를 통합시켜주는 성배와 같다.
- 타입유추 : 컴파일러가 스스로 타입을 유추할 수 있는 장소에서는 프로그래머가 구태여 타입 정보를 써넣지 않아도 되도록 만듬
- IEnumerable<int> range = Enumerable.Range(1,2,3,4,5); => var range = Enumerable.Range(1,2,3,4,5);
- 웨그너 , 변수의 구체적인 타입을 알 필요가 없는 곳이라면 가독성과 상관없이 최대한 var 키워드를 사용하는 편이 낮다.
- 프로그래밍 언어의 전체적인 방향은 타입유추를 최대한 활용해서 소스코드에 등장하는 타입 정보를 최소화 하는 방향으로
- C, C++, JAVA, C#과 같은 언어는 정적타이핑 시스템 사용, 루비 , 파이썬, 자바스크립트는 동적 타이핑 시스템을 사용
- Collection 의 메서드를 람다표현 식으로 구현 후 더하는 일은 C# 확장 메서드를 사용해서 해결
- <T> 인터페이스만 있으면 람다를 이요하는 링큐의 메[서드를 모두 마음껏 이용할 수 있다.
public static void Foo( This IEnumerable<T> source, int n )
{
Console.Out.Writeline("Foo number = " + n );
}
IEnumerable<int> list = new List<int>();
list.Foo(3);
- Function과 Method 동일한 개념이 아니다. 함수는 ' 일급'으로서 독자적으로 존재하는 코드, 메서드를 객체에 소속된 속성을 의미
- C#같은 객체지향 언어는 순수한 의미에서의 함수를 가지고 있지 않으며 그들은 모두 객체에 소속된 메서드다.
- 스칼라
- 가디언
- 가디어 신문사는 스칼라와 플레이를 사용해서 대단히 성공적이 프로젝트를 진행
- 스칼라 코드는 대단히 안정적인 자바 가상 기계에서 작동하는 자바 바이트코드로 컴파일되고, JVM의 튼튼한 저스트-인-차임 컴파일, 가비지 컬렉션, 그리고 널리 이용되는 전개테크닉을 그대로 활용한다.
- 스칼라 개발자를 찾으려고 하지 않았다. 그저 뭔가 새로운 것, 자신의 경력에 도움이 되는 새로운 무어을 배우려는 열망이 가득한 열린 마음을 가진 개발자를 찾으려고 했다.
- '동시성' 프로그래밍 혹은 '병렬' 프로그래밍은 누구도 피할 수 없는 시대적 화두과 되었다. 여기서는 단순히 Lock이나 Synchronized를 둘러싼 API를 향상시키는 정도의 노력으로 해결할 수준이 아니나 새로운 패러다임을 요구
- 멀티코어, 빅테이터, 분산처리라는 화두는 기존의 객체지향 기법만으로는 해결하기 어려운 새로운 문제에 해당한다. 이 문제는 함수형 프로그래밍이라는 새로운 패러다임의 등장을 요구했다.
- 제임스 스트라칸 2009년 7월 6일 월요일
- 그루비를 개발한 스트라칸은 내가 스칼리를 보았더라면 그루비를 개발하지 않았을 것이다라고 말함
- 멀티코어 프로세서를 대상으로 하는 프로그래밍이다. 그것은 정적인 타이핑을 사용하는 것이다. 그냥 파이썬, 루비, 그루지 책을 버려라. 자기 몸을 완전히 동적 프로그래밍 전영에 맡기고 있는 사라미 있으면, 다시 생각하기를 바란다.
- 트위터
- 링크드인이 그루비를 이용했다. 트위터가 스칼라 언어를 이용해서 개발되었다.
- 엉터리 일을 하면서 CPU만 뜨겁게 달구는 일이 없도록 실행 전에 미리컴파일이 되는 언어(정적언어)를 원했다. 스칼라
- 액터 : 큐 시스템을 이용하는 것과 같다.
- 마틴 오더스키
- 성급한 최적화는 모든 악의 근원이다 . 소프트웨어를 구현할 때 , 사용자가 필요호 하는 기능을 구현하는 데 집중하지 않고 처음부터 소프트웨어 성능을 최적화하기 위해서 서두르는 것은 겨로가적으로 엉터리 코드를 양산할 가능성이 크다는 사실 경고
- 프로그래머는 설계를 하면서 코드의 얼개를 작성하고 , 필요한 내용을 구현하며 살을 채워가고, 유닛테스트 코드를 통해서 그때그때 논리적 완결성을 증명하고, 필요하면 설계 자체를 수정하고, 그에 따르는 리펙토링을 수행한다. 이러한 사이클을 통해서 반복하면서 완성도가 점진적으로 높아지는 코드를 만든다.
- 객체 지향 패러다임을 구성하는 요소가 상속, 폴리모피즘, 캡슐화
- 함수형 프로그래밍 패러다임 구성하는 요소는 변경불가능성, 일급함수, 람다와 클로저, 고차함수, 재귀, 게으른 평가, 함수 합성, 모나드. => 코드의 간결함, 추상이다.
- 진짜 추상은 프로그래머가 주어진 문제를 해결하기 위해서 '사고를 하는 방식' 자체에 영향을 준다.
for (int index = 0; index < 10 ; index++) => for (Person p : persons) 와 같이
문법적 추상은 명령형 패러다임이라는 추상의 수준에서 벗어날 것은 아니다.
- 껍데기 추상 : 인터페이스(구현을 생략한 껍데기) , 람다 , 고계함수 , 전략패턴, 제네릭 등
- 커튼 추상 : 언어 자체의 문법이 아니라 라이브러리나 프레임워크를 이요행서 반복된 표현을 하지 않아도 되는 것
- Java 쓰레드 : Runnable, Callable interface를 이용해서 구체적인 업무(TASK) 정의한 다음 실행자(Executor)에게 제출, 쓰레드를 생성하고 소멸시키는 지질구레한 일은 집주인이 고용한 실행자들이 우리 눈에 보이지 않는 곳에서 해결.
- 지금까지의 멀티쓰레딩 프로그래밍이 '동시성'(Concurrently)을 대상으로 했다면 => Batch 수량이 감소하는 것 동시에 UI에 표현오늘날의 멀티쓰레딩은 '병렬성'(In parallel)을 대상으로 하게 되었다. => 데이타 처리를 병렬로 빠르게처리
- 동시성 : 표면적으로는 같은 시간대에 일어나지만 일 사이에서 문맥교환 Context switch가 분주하게 일어나는 것
- 병렬성 : 사람의 몸이 복제되어 물리적으로 두 사람이 존재. 두 가지 일은 서로에게 영향을 주지 않으면서, 즉 문맥교환을 수행할 필요가 없는 상태에서 문자 그대로 한번에 일어날 수 있는 일
- 2000년대 중반, CPU는 1, 2개 / 쓰레드를 이용한 동시성 코드를 작성하는 것은 CPU를 공유한다는 것, 문맥교환을 피할수 없음
- 현재 16, 32 코어의 데스크탑 PC or 분산환경 시스템/ 프로그래머가 늘러난 CPU를 모두사용하는 병렬처리 프로그램 개발은 불가능하다. 그러므로 커튼을 치자. 환경이 커튼 안에서 복작성을 다루는 동안 우리는 커튼 빡에서 단순함을 즐기면 된다.
- 하지만 개선된 API와 라이브러기를 컴포넌트 사용하는 것으로는 우리가 가진 문제를 모두 해결할 수 없다. 그러므로 커튼(실행환경) 바깥에서 작성하는 코드는 나중에 실행환경(커튼) 내부에서 복잡성을 다룰 사람들이 소비할 수 있는 방식으로 작성될 필요가 있다. 그래서 우리는 어떻게가 생략되고 무엇만이 담긴 선언적인(Declarative) 방식을 사용해야 된다.
C# TPL - TASK Parallel Library / 선언적 크로그래밍이 같는 수많은 장점의 예
- 하나의 쓰레드를 사용
from emp in employee
where emp.id > 100
select emp.salary:
- 하지만 employee table의 데이타가 100만행이고 16개 중 15개의 CPU가 놀고 있다. 병렬처리를 라는 로직을 구현해야 하지만
아래 코드르 간단히 해결할 수 있다.
from emp in employee.AsParallel()
where emp.id > 100
select emp.salary;
- 트레이트
- 모나드를 잘 알아두면 도움이 되는 두가지 측면
- 타입시스템이나 함수합성에 대한 이해가 깊어지기 때문에 객체를 설계하거나 프로그램의 모듈을 구성할 때 확실히 유연하고 확장성이 있는 시스템을 구현할 수 있게 된다.
- 모나드가 지적인 수전을 검증하는 리트머스처러 통용된다.
- 사용하는 언어에 새로운 기능이 추가될 때, 문법적인 측면에서만 접근하는 것은 좋지 않다. 그렇게 하면 그들이 ' 어떤문제'를 해결하기 위해서 고안이 되었는지 그 문제를 해결하는데 '왜' 도움이 되는지 못한 상태에서 암기하게 된다. => 왜 람다를 쓰는지 항상 궁금했었다. 어떤 장점이 있는지. 이 책에서 알게되었지만. ^^
- Person 이라는 interface가 있고 그것을 구현하는 클래스가 10개가 있다고 하자. 만약 eat() 이라는 메서드를 추가해야 한다면
- 구현클래스에 eat() 같은 내용을 구현 => DRY 원리 위반
- 추상클래스를 구현해서 넣는다고 하자. 하지만 채식주의자를 위한 eat() , 고기를 섭취하는 사람을 위한 eat()을 구현한다면?
- 여기서 어떤 사람은 어떤 사람은 생선과 계란을 먹는 채식주의, 어떤 사람은 돼지 고기만 먹는다면 ?
- 이렇게 까다로운 분류를 인터페이스-추상클래스-하위클래싀 공식에 대입해서 설계하는 것은 어렵고 불안정한 일이다.
- 코드의 복작성을 제거해주기 위해 서용되는 객체지향의 상속구조 자체가 오히려 복작성을 증폭시키는 역설이 일어난다.
- C#이 가진 확장메서드로 이 문제를 쉽게 해결할 수 있다. 하위클래스에 손을 대지 않으면서 인터페이스 안에 구체적인 메서드의 구현 내용을 추가할 수 있다. 자바에서라면 반드시 구현 클래스에서 메서드의 정의와 구현을 해야한다.
// Person에 확장메서드 추가
Public static void Eat(this Person p) {
Console.Out.WriteLine(" I'm eating...");
}
// 구현 클래스를 수정할 필요가 없다. 야호
Person p = new HungryMan()
p.Eat();
- 하지만 다양한 식성에 맞는 Eat을 구현해야 한다면?? C#의 확장메서드로는 충분하지 않다.
//고기를 먹지만 돼지고기는 먹지 않는 IeatAnythingButPort 클래스의 eat()
public override void Eat() {
Console.Out.WriteLine("I eat everything");
Console.Out.WriteLine("Except for port. ");
}
// 채식 주의자지만 계란이나 우유는 먹는 IamVegerarianButCnaEatDiaryFood Class
public override void Eat() {
Console.Out.WriteLine("I eat vegitables only");
Console.Out.WriteLine("With eggs and milk. ");
}
- 위와 같이 다양한 Eat() 메서드를 모두 Person 인터페이스에 넣을 수 없고, Override 해서 억지로라도 가능하지만 코드가 너무 지저분해진다. 그러므로 하위클래스의 소스코드를 하나씩 열어서 Eat() 메서들 내용을 작성하는 편이 안전.
- C#의 확장메서드는 인터페이스를 사후적으로 코드를 주입해야 할 때는 강력한 도움을 주지만 , 인터페이스라는 구조물 자체가 가지고 있는 한계로부터 자유롭지 않다.
- C# , JAVA의 인터페이스가 갖는 문제는 어떤 클래스가 그것을 구현하는 순간 그것이 정의하고 있는 API를 받아들여야 한다.
- 우리가 원하는 것은 자동자나 , 스마트폰에게 Person 이 가지고 있는 eat() 특정 특질(Trait)를 전달 하는 것이다. 이렇게 어떤 특정한 특질을 주입하는 것을 전문용어로 '섞어 넣기 mix-in' 이라고 한다.
- 자바나 C#에서 사용하는 인터레이스라는 구조물이 타입안정성을 보장하기 위해서 사용되는 정적 타입시스템과 관련이 깊은 개념
- 믹신은 동적프로그래밍 언어에서 애용되는 구조물 . 마틴 오더스키는 스칼라를 설계하면서 믹신과 거의 같은 트레이트를 도입
- 에필로그