인터페이스 클래스 상속 - inteopeiseu keullaeseu sangsog

상속이 없다면 공통적인 특성이 있음에도 각각의 class 마다 Method와 상태값을 정의해요 한다. 이를 상속을 이용하면 공통적인 특징을 정의하는 부모 클래스를 만들고 자식 클래스에서 부모의 기능을 물려받을 수 있다.

public class Car { bool powerOn; public void Start(){ } public void ShutOff(){ } public void Accelerator(){ } public void Brake(){ } } public class Tesla : Car { bool electric; } public class Hyendai : Car { }

C#에서는 콜론( : )을 이용해서 부모 클래스의 기능을 물려받을 수 있다. C#에서는 단일 상속만 가능하다. Car라는 클래스를 부모 클래스로 받았으면, 다른 클래스는 부모 클래스로 상속받을 수 없다. 

2. abstract

Car라는 클래스는 단일로는 생성해도 의미가 없는 인스턴스이다. 그렇기 때문에 Car 라는 부모 클래스는 인스턴스는 생성하지 못하지만 Car가 가지고 있는 특정 Method에 대해서는 자식들이 반드시 재정의하도록 강제적으로 하고 싶을 수도 있다. 이를 위한 방법이 추상 클래스(abstract class)와 추상 메서드(abstract method)이다. 추상 메서드는 abstract 예약어가 지정되고 구현 코드가 없는 Method이다. 추상 메서드는 반드시 추상 클래스 안에서만 선언할 수 있으며, 추상 메서드를 하나라도 가지고 있으면 추상 클래스로 만들어줘야 한다는 말이다. 

2.1 abstract class 특징

  • 추상 메서드에는 접근 제한자로 private를 사용할 수 없다.
  • new를 사용해 인스턴스로 만들 수 없다
  • 추상 메서드만을 가질 수 있다. 

2.2 abstract class 예제

Employee라는 class를 만들고 직원의 개인 ID 번호와 연봉을 입력 받도록 한다. 그리고 추상 메서드로는 월급을 계산하는 함수인 CalcMonthSalary를 추가했다. 

abstract class Employee { public int EmpID { get; private set; } public long Salary { get; set; } public Employee(int empId, long salary) { EmpID = empId; Salary = salary; } public abstract long CalcMonthSalary(); public virtual long CalcMonthSalary_() //일반 Method도 구현할 수 있다. { return this.Salary / 12; } }

다음으로는, Employee라는 부모 클래스를 상속받는 영업직 직원 class를 만들어보자. 영업직 직원은 incentive를 따로 지정해서 연봉에 추가해주도록 하자.

class SalesPerson : Employee { public double Incentive { get; set; } public SalesPerson(int empid, long salary, double incentive):base(empid, salary) { this.Incentive = incentive; } public override long CalcMonthSalary() { return this.Salary / 12 + (long)(this.Salary / 12 * Incentive * 0.01); } }

부모 클래스를 추상 클래스로 만들었고 해당 클래스에는 추상 메서드가 구현되어 있다. 그러므로 상속받는 자식 클래스에 해당 메서드에 대한 구현이 없으면 에러가 난다.

class abstract_ex1 { static void Main() { Employee emp1 = new SalesPerson(1000, 30000000, 0); Console.WriteLine(emp1.CalcMonthSalary()); } }

Employee는 부모 클래스이기 때문에 자동으로 Class 형변환이 되어 Employee로 만들 수 있다. 추상 클래스는 new를 할 수 없기 때문에 자식 클래스로 new를 하여 인스턴스를 생성한다.
C#에서는 단일 상속만 가능하고 했는데, 한가지가 아닌 여러 가지를 상속받아서 사용하고 싶을 때고 있을 것이다. 이럴 때 사용하는 게 인터페이스이다.

3. interface

인터페이스는 계약이라고 볼 수 있다. interface는 클래스가 아니기 때문에 다중 상속이 가능하다. 인터페이스는 추상 메서드만 0개 이상 담고 있는 추상 클래스라고 봐도 무방하다.

관련글

  • [Java] String 비교 방법 (equals, == 연산자, compare) 2021.11.17

  • [Java] 자바 쇼트 서킷 (short-circuit) 2021.11.17

  • [java] JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가 2021.11.11

  • [Java] Call by value, Call by reference (feat. new 연산자) 2021.04.25

댓글 0

+ 이전 댓글 더보기

비공개 댓글 남기기

일반적인 프로그래밍 언어에서는 유전적 상속은 단 하나의 클래스로부터만 가능합니다. 하지만, 인터페이스의 경우에는 다중 상속이 가능합니다. 프로그래밍을 한다고 해서 피아노를 배우지 못하란 법은 없습니다.

프로그래밍을 하는데 있어서도 이렇게 관계를 분리해야 할 경우가 있습니다. 그러나, 칼로 자르듯이 "이런 경우에는 반드시 상속(is-a)를 통해서 해결해야 하고, 저런 경우에는 인터페이스(can-do)를 이용해서 해결해야 한다"라고 말하기는 어렵습니다. 설계자가 어느 정도 상황을 인지하여 결정을 내리는 수 밖에 없습니다. 다만, 최대한 자신의 개성이 아닌 보편적으로 이해 될 수 있는 구조를 갖출 수록 값진 설계가 될 것 입니다.

프로그래밍 환경에서 다시 살펴보기

애완동물 샵 관리 프로그램을 작성하려는데, 현재 샵에서 판매하고 있는 동물들은 [그림 2]와 같습니다.

[그림 4]

[그림 4]처럼 요구사항을 처리했으나, 이번에는 "기어다니기"가 중복되고 있습니다. 이번에도 상속을 통해서 처리하도록 하겠습니다.

[그림 5]

[그림 5]와 같이 이번에도 요구사항을 처리했으나 또 다시 새로운 요구사항이 들어 옵니다. (이 바닥이 원래 그렇잖아요 ㅡ.ㅡ) 취급 동물이 하나 더 늘었습니다. 이번에 취급해야 하는 것은 오리 입니다.

[그림 6]

[그림 6]을 보시면 아시겠지만, 곤란한 문제가 생겼습니다. 오리는 육상동물 행동인 "기어다니기"도 하고, 수중동물인 금붕어와 같이 "헤엄치기"도 합니다. 양쪽의 형질을 모두 이어 받으려면 다중상속이 필요한 순간입니다. 다중상속은 대부분의 언어가 지원하지도 않고, 지원한다고 해도 무분별하게 사용하면 추후 감당하기 어려운 문제에 봉착하게 됩니다.

[그림 6]의 경우에는 상속이 두 단계에 거쳐서 일어나면서 "최대한 상속으로 피해라"라는 원칙에도 위배 됩니다. (상속은 장점도 있지만, 그에 따른 비용이 큽니다)

[그림 7]

[그림 7]은 이러한 단점들을 인터페이스를 통해서 처리한 내용입니다. 즉, "기어다니기"와 "헤엄치기"를 유전적 형질로 보지 않고, 후천적 형질로 보고 처리한 것 입니다. 상속의 단계도 줄어들었고, 다중상속 문제도 해소되었습니다. 앞으로 추가적인 형질이 늘어난다고 해도, 상속 단계를 늘리지 않고 해결 할 수 있는 구조입니다.

오리가 "나는 기어 다닐수도 있고, 헤엄칠 수도 있다" 라고 말하고 있습니다.

그러나, 여기에도 문제는 있습니다. 인터페이스는 코드를 공유하지 않고 단지 인터페이스만 공유하는 것 입니다. 결국 "기어다니기"와 "헤엄치기" 코드는 모든 클래스에 중복되어 구현되고 있습니다.

[그림 8]

[그림 8]에서는 중복되는 "기어다니기"와 "헤엄치기" 코를 각각 "기는알고리즘" 클래스와 "헤엄치기알고리즘" 클래스에 옮겨두고, 각각의 애완동물 클래스들은 이 클래스에게 해당 기능을 위임하여 처리하고 있습니다.

정리

상속과 인터페이스 구현이 왜 필요한 지를 생각해 보겠습니다.

  • 코드의 공유
    • 상속은 중복된 코드를 제거하여 이를 상속 받는 클래스들이 코드를 공유 할 수 있도록 합니다.
  • 인터페이스의 공유
    • OOP로 프로그래밍을 하다보면 수 많은 객체들을 다루게 됩니다. 만약 특정 객체들이 동일한 호출방식(인터페이스)를 갖고 있다면, 객체의 타입을 무시하고 이를 호출 할 수 있게 됩니다. 이것을 다형성이라고 부르며, 조건문을 제거하고도 객체의 종류의 따라 같은 제어(흐름)에서 다른 결과를 얻을 수 있게 됩니다.

하지만, 코드의 공유는 위임을 통해서도 처리 할 수 있기 때문에, 상속보다는 위임을 우선으로 생각해보는 것이 좋습니다.

인터페이스의 공유는 클래스 상속과 인터페이스 구현 모두 가능합니다. 다만, 상속의 경우에는 다중 상속의 벽에 부디칠 수도 있고, 남발하면 코드의 유지 보수가 어려워 질 수 있습니다. 결국, 클래스 상속과 인터페이스 구현 중 하나를 선택해야 하며, 그 기준은 해당 관계가 "is-a" 인가 또는 "can-do" 인가를 보고 결정하시면 됩니다.

Toplist

최신 우편물

태그