삽질의 현장/- .NET

#049_닷넷(.NET)_.Net Framework 기본 - IComparable & IComparer

shovelman 2015. 10. 30. 20:27


안녕하세요 삽잡이입니다.


이번 시간에는 닷넷에서 많이 사용하는 인터페이스의 형식중

IComparable 과 IComparer 에 대해서 알아보려고합니다.


IComparable은 '나랑 너랑' 비교입니다.

그리고 IComparer는 '제 3자'끼리 비교하는 인터페이스입니다.


IComparable 인터페이스에는 int형을 반환하고, 

object형식의 인자를 받는 CompareTo() 메서드가 있습니다.



즉, 나와 상대 객체와 비교를 하겠다는 것입니다.

보통 반환 형은 내가 크면 1, 상대가 크면 -1, 같으면 0을 반환합니다.


IComparer은 int형을 반환하며, object형 인자를 두개 받는..

즉, 두개의 object형식을 비교하는 Compare() 메서드가 있습니다.



두 인터페이스는 모두 같이 비교를 하는 것입니다.

단, 비교 대상이 나와 상대인지, 아니면 제 3자의 객체들인지 나뉘는 것이지요...




sort라는 메서드는 비교를 할 수 있어야함을 말합니다.

각 원소끼리 상대적으로 비교를 하지 못하면 정렬을 할 수 없겠지요.

따라서 int형 배열은 ICompareable 인터페이스를 가집니다.


그런데 뜬금 없이 값자기 'Sap[]' 이던지 'Food[]'

이런 형식들을 Sort로 넘기면 비교가 될까요?


기본적으로 정수와 정수는 크기를 비교하지만,

저 이상한 Sap, Food는 비교가 안되지 않습니까?

따라서, 해당 객체가 정의된 클래스에 Comparable을 구현해주면 됩니다.


여기서 우리는 사실 하나를 알게됬습니다.

바로, Sort 메서드 안에는 내부적으로 IComparable이 구현되어있는지 확인한다는 것입니다.

즉, 구현이 되어있다면 CompareTo() 메서드를 통해 정렬을 위해 비교한다 이겁니다.


그런데 왜 IComparable 와 IComparer를 나눠놨을까요?

만약 비교 정책이 많다면?

여러개의 비교정책을 가지고 싶다면?

여러분을 비교하고자하는 객체를 고치겠습니까?

그래서 IComparer 인터페이스가 제공되는 것입니다.


캡슐화란 공통적인 모든 것들을 모으는 것 같이 느껴지시지요?

그런데 제 3자에 이 비교하는 기능을 떼어버린다면?

오히려 캡슐화가 더 좋아집니다.


예를 들어서 Car 집합이 있다면,

이 Car라는 클래스를 고칠 필요 없이 제 3자에 비교하는 정책을 사용해서 

Car 객체를 정렬할 수 있다 이겁니다.

그 것이 바로 IComparer 인터페이스이구요.


왜 캡슐화 이야기를 꺼냈을까요?

IComparable 처럼 내부에 꼭 묶여있는 것이 더욱 바람직한 캡슐화일까요?

아니면, IComparer와 같이 밖에 떨어져 있는 것이 바람직한 캡슐화일까요?

물론, case by case입니다.

그런데, 후자가 저는 더 좋아보입니다.


캡슐화를 하는 목적은 잘 안보이게 하나의 단위로 묶어놓음으로써 

많은 것을 알리지 말자는 것입니다.

즉, 유연성이 커지게 하는 것이 핵심이지요.

조금만 알고 있으면 바꾸기 쉬우니깐요!


이렇게 기능을 떼 놓는다면 비교 기능이, 비교 정책이 Car의 것이 아니라고 해도

언제든지 Car에 정렬을 하고자할 때에는 

IComparer 인터페이스가 구현된 객체들을 사용함으로써 유연성이 훨씬 커진다는 소리입니다.


그리고 캡슐화는 핵심적인 것들끼리 잘 묶여있어야, 

잘 추상화 되어있어야 진정한 캡슐화입니다.

Car 객체를 정의하는데 Car들의 비교를 Car 클래스가 가져야할까요?

굳이 가질 필요가 있을까요?


비교 정책을 Car가 가지게 되면 비교정책 자체가 Car의 것이 됩니다.

그런데, 만약 1000대의 Car중에서 단 한대의 Car만이 비교를 원한다면?
상식적으로 Car 클래스에는 비교정책에 대한 정의가 없는것이 바람직합니다.

즉, Car라는 클래스는 비교될 수 없는이라는 추상화가 깔려있다는게 좋다는 소리입니다.


그런데 단 한대라도 비교를 원한다면,

Car끼리의 비교를 원하기 때문에, 외부에서 Car 객체 끼리 비교할 수 있도록

IComparer 인터페이스로 약속을 해주자 이겁니다.


어찌됬건,

IComparable 인터페이스는 내 객체와 다른 객체의 비교를 '직접'하는 것입니다.

IComparer 인터페이스는 제 3자를 통해 비교를 하는 것입니다.

따라서, IComparer 인터페이스에는 this 객체라는 개념이 없습니다.


따라서 Sort() 메서드의 인수로 IComparer 인수를 받을 수 있습니다.



그런데 이문장을 이와 같이도 사용할 수 있습니다.



정적 속성을 사용함으로써 비교를 할 수 있지요...

아까의 new를 사용할 때와 같습니다.


IComparer 인터페이슬 여러 객체에서 구현을 해서 메서드가 여러개 있다면,

각각 정렬 규칙들이 다를 것입니다.

이때, 각 정렬 규칙을 만들 수 있겠지요.



그런데 만약, 정적 속성을 사용하여 비교를 하게 된다면?



정적 속성을 사용할 경우에는 직접 객체를 건드리게 됩니다.


예를들어 Food라는 객체가 다 완성되있는 상태라고 생각해보겠습니다.

그런데, 이 Food 객체를 내림 차순 정렬이 하고싶어졌습니다.

이때에 SortByDown 이런식으로 정렬 규칙을 만들고 싶다고 해보겠습니다.


Food 객체는 이미 만들어져있는 객체로써,

나는 Food 객체를 가져다 쓰는 사용자이니까 IComparer을 통해 구현을 하면 

이처럼 사용할 수 있게 됩니다.


 

그러면, Food.SortA와 같이 속성을 사용하지는 못하지 않습니까?


그런데, new를 통해 연관성이 있다는 사실을 어디서도 확인할 수 없습니다.

따라서 Food.SortA와 같이 정책 설정을 제공하는 객체의 경우

정적 속성을 제공함으로써 해당 객체의 것 즉, 연관성이 있다는 사실을 확인할 수 있습니다.


만약, 사용자가 기존에 없는 비교 정책을 사용하고 싶다면,

만들어서 new로 사용하면 되겠지요.

어차피 정적 속성은 어떠한 정책으로 비교하겠다는 것을 정해놓은 것 뿐입니다.


이번 시간은 여기까지 하도록 하겠습니다.


이상 삽잡이였습니다!