웹사이트 검색

구성 대 상속


구성 대 상속은 자주 묻는 인터뷰 질문 중 하나입니다. 상속보다 구성을 사용하는 방법도 들어 보셨을 것입니다.

구성 대 상속

구성과 상속은 모두 개체 지향 프로그래밍 개념입니다. Java와 같은 특정 프로그래밍 언어에 얽매이지 않습니다. 프로그래밍 방식으로 상속에 대한 구성을 비교하기 전에 이에 대한 빠른 정의를 살펴보겠습니다.

구성

컴포지션은 개체 간의 관계를 구현하기 위한 개체 지향 프로그래밍의 설계 기법입니다. Java의 구성은 다른 객체의 인스턴스 변수를 사용하여 이루어집니다. 예를 들어 Job을 가지고 있는 사람은 자바 객체지향 프로그래밍에서 아래와 같이 구현된다.

package com.journaldev.composition;

public class Job {
// variables, methods etc.
}
package com.journaldev.composition;

public class Person {

    //composition has-a relationship
    private Job job;

    //variables, methods, constructors etc. object-oriented

계승

상속은 개체 간의 is-a 관계를 구현하기 위한 개체 지향 프로그래밍의 설계 기술입니다. Java의 상속은 extends 키워드를 사용하여 구현됩니다. 예를 들어 Java 프로그래밍에서 Cat is an Animal 관계는 아래와 같이 구현됩니다.

package com.journaldev.inheritance;
 
public class Animal {
// variables, methods etc.
}
package com.journaldev.inheritance;
 
public class Cat extends Animal{
}

상속보다 구성

구성과 상속 모두 서로 다른 접근 방식을 통해 코드 재사용을 촉진합니다. 그래서 어느 것을 선택해야 할까요? 구성과 상속을 비교하는 방법. 프로그래밍에서 상속보다 구성을 선호해야 한다는 말을 들어보셨을 것입니다. 구성과 상속을 선택하는 데 도움이 되는 몇 가지 이유를 살펴보겠습니다.

  1. Inheritance is tightly coupled whereas composition is loosely coupled. Let’s assume we have below classes with inheritance.

    package com.journaldev.java.examples;
    
    public class ClassA {
    
    	public void foo(){	
    	}
    }
    
    class ClassB extends ClassA{
    	public void bar(){
    		
    	}
    }
    

    For simplicity, we have both the superclass and subclass in a single package. But mostly they will be in the separate codebase. There could be many classes extending the superclass ClassA. A very common example of this situation is extending the Exception class. Now let’s say ClassA implementation is changed like below, a new method bar() is added.

    package com.journaldev.java.examples;
    
    public class ClassA {
    
    	public void foo(){	
    	}
    	
    	public int bar(){
    		return 0;
    	}
    }
    

    As soon as you start using new ClassA implementation, you will get compile time error in ClassB as The return type is incompatible with ClassA.bar(). The solution would be to change either the superclass or the subclass bar() method to make them compatible. If you would have used Composition over inheritance, you will never face this problem. A simple example of ClassB implementation using Composition can be as below.

    class ClassB{
    	ClassA classA = new ClassA();
    	
    	public void bar(){
    		classA.foo();
    		classA.bar();
    	}
    }
    
  2. There is no access control in inheritance whereas access can be restricted in composition. We expose all the superclass methods to the other classes having access to subclass. So if a new method is introduced or there are security holes in the superclass, subclass becomes vulnerable. Since in composition we choose which methods to use, it’s more secure than inheritance. For example, we can provide ClassA foo() method exposure to other classes using below code in ClassB.

    class ClassB {
    	
    	ClassA classA = new ClassA();
    	
    	public void foo(){
    		classA.foo();
    	}
    	
    	public void bar(){	
    	}
    	
    }
    

    This is one of the major advantage of composition over inheritance.

  3. Composition provides flexibility in invocation of methods that is useful with multiple subclass scenario. For example, let’s say we have below inheritance scenario.

    abstract class Abs {
    	abstract void foo();
    }
    
    public class ClassA extends Abs{
    
    	public void foo(){	
    	}
    	
    }
    
    class ClassB extends Abs{
    		
    	public void foo(){
    	}
    	
    }
    
    class Test {
    	
    	ClassA a = new ClassA();
    	ClassB b = new ClassB();
    
    	public void test(){
    		a.foo();
    		b.foo();
    	}
    }
    

    So what if there are more subclasses, will composition make our code ugly by having one instance for each subclass? No, we can rewrite the Test class like below.

    class Test {
    	Abs obj = null;
    	
    	Test1(Abs o){
    		this.obj = o;
    	}
    	
    	public void foo(){
    		this.obj.foo();
    	}
    
    }
    

    This will give you the flexibility to use any subclass based on the object used in the constructor.

  4. One more benefit of composition over inheritance is testing scope. Unit testing is easy in composition because we know what all methods we are using from another class. We can mock it up for testing whereas in inheritance we depend heavily on superclass and don’t know what all methods of superclass will be used. So we will have to test all the methods of the superclass. This is extra work and we need to do it unnecessarily because of inheritance.

구성 대 상속에 대한 모든 것입니다. 상속보다 구성을 선택해야 할 충분한 이유가 있습니다. 상위 클래스가 변경되지 않을 것이라고 확신하는 경우에만 상속을 사용하고, 그렇지 않으면 합성을 수행하십시오.