본문 바로가기

Java/Java SE, EE

인터페이스를 구현하는 *Impl, 관습적인 추상화에 대한 이해

이 문서의 내용

    Impl 클래스란?

    자바의 여러 라이브러리의 구현 내용을 살펴보면 *Impl.class와 같은 네이밍의 파일을 종종 볼 수 있습니다.

    public class ParserImpl implement Parser
    {
    	...
    }

    정확한 명칭은 없으나 편의상 Impl 클래스라고 부르며, 인터페이스를 구현(Implementation)하는 클래스의 의미로 사용합니다.

    자바에서 많이 사용되는 관습적인 추상화에 불과하며 다른 언어 환경 또는 다른 네이밍으로 Impl 클래스를 사용해도 무관합니다.

    첫 번째 목적: 인터페이스로부터 구현체를 독립

    Impl 클래스를 사용하는 이유는 인터페이스와 구현 코드를 분리함으로써 구현체를 독립적으로 확장하기 위함입니다.

    public interface Parser
    {
    	public void parse();
    }
    
    public class XMLParser implements Parser
    {
    	public void parse()
    	{
    		// prepare parse
    		// do xml parse
    	}
    }
    
    public class JSONParser implements Parser
    {
    	public void parse()
    	{
    		// prepare parse
    		// do json parse
    	}
    }

    예를 들어 Parser 인터페이스를 구현하는 두 개의 XMLParser JSONParser가 있습니다.

    Parser 인터페이스가 정의하는 parse()에서 prepare parse 로직에 변경이 생기면 이를 구현하는 두 개의 클래스에도 코드 변경이 필요합니다.

    인터페이스로부터 클래스를 상속하는 패턴에서 이와 같은 문제는 빈번히 발생합니다. 하나의 인터페이스로부터 여러 개의 클래스가 파생되기 때문입니다.

    다음 예시에서는 ParserImpl 클래스를 정의하고 구현체를 독립하는 개선된 코드입니다.

    public interface Parser
    {
    	public void parse();
    }
    
    public class ParserImpl implements Parser
    {
    	protected void prepareParse()
    	{
    		// prepare parse
    	}
    }
    
    public class XMLParser extends ParserImpl
    {
    	public void parse()
    	{
    		prepareParse();
    		// do parse
    	}
    }
    
    public class JSONParser extends ParserImpl
    {
    	public void parse()
    	{
    		prepareParse();
    		// do parse
    	}
    }

    parse() 메소드에서 공통의 로직인 prepare parse를 ParserImpl 구현체에서 구현합니다.

    그리고 각각의 파생 클래스인 XMLParserJSONParser는 다형성에 의해서 각각에 대한 독립 로직을 구현하는 parse() 메소드에만 집중하게 됩니다.

    두 번째 목적: 공통의 기능을 유닛 테스트로 진행

    예시에서 공통된 prepare parse 로직은 ParserImpl 클래스에서 구현합니다.

    public class ParserImpl implements Parser
    {
    	protected void prepareParse()
    	{
    		// prepare parse
    	}
    }

    세부 동작에 대한 구현이 완성되어 있지 않더라도 공통된 비즈니스 로직을 유닛 테스트하기 위해 ParserImpl을 사용 할 수 있습니다.

    유닛 테스트가 진행되고 나서 안정성이 공통 코드에 대한 확보되었다면 ParserImpl을 abstract로 추상화하고 파생 클래스의 내용을 작성 할 수 있습니다.

    세 번째 목적: 협업에서의 업무 분담

    예시에서는 Parser 인터페이스로부터 파생되는 PaserImpl XMLParser JSONPaser 세 개의 클래스가 존재합니다.

    이를 세 명의 담당자가 동시 작업해야 한다면 다음과 같이 분담 될 수 있습니다.

    • 작업자 A는 공통된 비즈니스 로직 또는 코어 기능을 ParserImpl 구조체에서 작성
    • 작업자 BXMLParser에서 XML 파싱에 대한 비즈니스 로직을 작성
    • 작업자 CJSONParser에서 JSON  파싱에 대한 비즈니스 로직을 작성

    공통의 기능을 Parser 인터페이스에서 미리 정의하고, 서로간에 독립된 코드로 작성되기 때문에 충분히 가능한 상황입니다.

    (실제로도 이런 방식으로 분업이 이뤄지는 경우가 종종 있습니다)

    정리 및 복습

    • Impl 클래스인터페이스를 구현(Implementation)하는 클래스를 의미합니다.
    • Impl 클래스의 네이밍은 관습적으로 사용되는 것이며, 네이밍 방식 자체는 큰 의미를 갖지는 않습니다.
    • Impl 클래스를 사용하면 인터페이스로부터 구현체를 독립 할 수 있어 구현체 코드 변경에 비교적 자유롭습니다.
    • 또한 공통의 기능을 구현체에서 먼저 구현하여 유닛 테스트가 가능합니다.
    • 테스트가 완료된 Impl 클래스는 abstract로 추상화 할 수 있습니다.
    • 구현체와 파생 클래스 간에 비즈니스 로직이 독립적으로 구현되므로 협업 과정에서 업무 분담에 용이합니다.