Service Provider Interface

 

SPI, 서비스 제공자 인터페이스 개념

 

 

 

Service Provider Interface는 Service Provider Framework의 한 구성요소입니다. 

 

Service Provider Framework는 확장 가능한 Java Application을 만드는 패턴 중 하나입니다.

 

외부에서 클라이언트에게 Jar파일을 받아, 기존 코드의 변경 없이 기능을 확장할 수 있습니다.

 

 

대표적으로 Java EE(Enterprize Edition)의 핵심 기술 중 하나인 JDBC가 이 패턴을 활용하고 있습니다. 

 

Java Database Connection

 

JDBC가 SPI로 구현된 덕분에

 

우리가 Java 코드로 Database와 Connection을 연결할 때에도

 

Oracle, Mysql, Mssql 등의 모든 드라이버 코드를 먼저 구현하는 것이 아니라, 

 

핵심적인 코드만 남겨두고 각각의 데이터베이스 회사에서 만든 JAR파일을 받아 넣어 사용할 수 있는 것입니다. 

 

기존의 Java 코드는 변화하지 않으면서, 클라이언트가 만든 파일을 통해서 기능을 확장시켜 구현할 수 있고, 

 

만약 클라이언트가 Driver를 넣지 않으면 기본적인 Driver 객체를 반환하여 연결할 수 도 있습니다. 

 

즉, 최소한의 조건 (SPI Interface의 구현) 만을 지키면 얼마든지 클라이언트가 새로운 객체를 만들어서 

 

자유롭게 데이터베이스 연결을 다룰 수 있는 것입니다. 

 

 

SPI의 필수요소 세 가지

 

SPI 를 구현하기 위해서는 핵심적으로 세 가지의 요소가 필요합니다.

 

Service, 변하지 않는 기능

 

먼저 기능을 수행하는 변하지 않는 코드가 필요합니다.

 

Service Provider Interface는 기능을 확장하기 위하여 사용하는 것이므로 

 

사용자가 아무런 값을 넣지 않아도 기본적으로 실행되는 기능이 존재해야 합니다. 

 

또, 이 기능은 외부 Resource(JAR파일) 에 의해서 바뀌지 않아야 합니다. 

 

Service Provider Interface (SPI),

확장기능에 필요한 최소한의 조건을 명시하는 인터페이스 

 

 

만약 기능을 확장하고 싶다면, 최소한 SPI로 명시된 인터페이스를 Implement하여 구현체를 만들고 

 

추상메서드를 오버라이드 하여 필수 조건을 충족시켜야 합니다. 

 

인터페이스를 통해서 구현하기 때문에, 인터페이스에 있는 최소한의 조건 ( 추상메서드 구현 ) 만 맞추면 

 

다른 코드의 작동과 상관없이 앞의 "Service( 변치 않는 기능 )" 을 사용할 수 있도록 구현해야 합니다. 

 

Service Provider, SPI의 구현체 

 

클라이언트가 SPI를 구현하여 확장시킨 SPI의 구현체입니다. 사용자가 자유롭게 커스터마이징하여 기존의 기능에서 

 

확장된 또다른 기능을 코드의 변경없이 제공할 수 있는 리소스입니다. 

 

 

 

 

 

SPI Tutorial Oracle Documentation

( 오라클 SPI 튜토리얼 문서 ) 

 

오라클에서 제공하는 사전을 만드는 예제를 통해서 SPI 패턴을 실습 해 봅시다. 

 

 

 

Creating Extensible Applications (The Java™ Tutorials > The Extension Mechanism > Creating and Using Exten

The Java Tutorials have been written for JDK 8. Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available. See Java Language Changes for a summary of updated

docs.oracle.com

 

위의 링크에 들어가서 DictionaryServiceDemo.zip 파일을 다운로드 받아 열어보면 다음과 같은 구조를 가집니다. 

 

DictionaryDemo, Ant 빌드 툴을 사용하여 모든 사전을 불러온 후, 검색기능을 사용합니다.

 

DictionaryServiceProvider, SPF의 기본 인터페이스와 클래스(Service, SPI)를 구성합니다.

 

ExtendedDictionary, SPI를 Implement하여 구현한 확장 사전입니다.

 

GeneralDictionary, SPI를 Implement하여 구현한 일반 사전입니다.

 

 

DictionarySerivce, Service 로직

 

DictionaryService 의 코드 구조를 보면, 핵심 로직은 단 하나, getDefinition(String word)라는 메서드로, 

 

Dictionary라는 클래스를 가진 ServiceLoader<Dictionary>를 호출하여 

 

Dictionary 클래스들을 돌아가면서 그 안의 getDefinition() 함수를 부르고, 

 

다음 사전이 있다면 같은 작업을 반복하는 것을 확인하실 수 있습니다.  

 

이 DictionaryService는 이 Service Provider Framework의 핵심 로직 중 하나로, 

 

어떤 외부 Dictionary ( 다른 사전, 즉 영어사전, 한문사전 등등.. ) 가 들어오건 간에  변형되지 않는 원본 코드입니다. 

 

public class DictionaryService {

    private static DictionaryServiceExample example;
    private ServiceLoader<Dictionary> loader = ServiceLoader.load(Dictionary.class);

		// 1. 생성자를 private 처리하여 일반 생성하지 못하도록 막는다. 
		// ** static synchronized를 이용하여 Singleton Pattern으로 하나의 객체만을 만든다.
    private DictionaryServiceExample() {}
    public static synchronized DictionaryServiceExample getInstance() {
        if (example == null) {
            example = new DictionaryServiceExample();
        }
        return example;
    }

    // 2. 모든 사전을 경유하며 단어를 찾는 핵심 서비스 로직이다.
    public String getDefinition(String word) {
        String definition = null;

        Dictionary d;
        try {
			// Dictionary 객체들을 경유하며 map에 특정 단어를 찾는다. 
            for(Iterator<Dictionary> dictionaries = this.loader.iterator(); definition == null && dictionaries.hasNext(); definition = d.getDefinition(word)) {
                d = (Dictionary)dictionaries.next();
            }
        } catch (ServiceConfigurationError var5) {
            definition = null;
            var5.printStackTrace();
        }
        return definition;
    }
}

 

 

 

Dictionary, 서비스 제공 인터페이스 (SPI) 

 

 

 

Dictionary는 클라이언트가 새로운 사전 ( 확장된 기능 ) 을 만들기 위해 반드시 구현해야 하는 인터페이스입니다. 

 

추상클래스 (Abstract Class) 로 getDefinition()이라는 메서드가 선언되어 있습니다. 

 

이 Dictionary를 implement 한다면 getDefinition() 메서드를 강제로 오버라이드 해야합니다. 

 

오버라이드 된 메서드를 구현한다면, 위의 "사전 탐색 서비스", 

 

Service 를 이용할 최소한의 조건을 충족한 것입니다. 

 

사용자는 복잡한 원본 코드 ( DictionaryService ) 를 신경 쓸 필요 없이

 

필요한 사전에 대한 인터페이스를 구현하기만 하면  얼마든지 자신이 원하는대로 사전을 추가할 수 있습니다. 

 

public interface Dictionary {
    public String getDefinition(String word);
}

 

 

 

ExtendedDictionary, 사용자가 추가로 구현한 SPI의 구현체 Service Provider 

 

 

 

아래의 코드처럼 확장 사전을 만드려면, Dictionary(SPI)를 구현한 다음 

 

강제 오버라이드 된 getDefinition만 구현해 주면 됩니다. 

 

 

//---------------------------- 확장 사전 ---------------------------------
public class ExtendedDictionary implements Dictionary {

        private SortedMap<String, String> map;

    // Map을 활용하여 단어를 넣는다. 
    public ExtendedDictionaryExample() {
        map = new TreeMap<String, String>();
        map.put(
            "xml",
            "a document standard often used in web services, among other " +
                "things");
        map.put(
            "REST",
            "an architecture style for creating, reading, updating, " +
                "and deleting data that attempts to use the common " +
                "vocabulary of the HTTP protocol; Representational State " +
                "Transfer");
    }

    @Override
    public String getDefinition(String word) {
        return map.get(word);
    }
}

 

 

Dynamic ClassLoading, Java 고유의 동적 컴파일기능 

 

 

이제, 이 SPI를 통해서 사용자가 자유롭게 기능을 확장시킬 준비가 거의 끝났습니다. 

 

그런데, 외부에 있는 Jar파일을 이미 컴파일된 자바 언어가 어떤 방식으로 다시 읽어들이는 걸까요? 

 

사실 이 ExtendedDictionary는 지금의 코드와 같은 형식이 아니라, 

 

JAR파일 ( Java 압축파일 ) 로 사용자가 폴더에 넣어주기 때문에 

 

현재 JVM은 실제로 이 클래스의 존재를 모릅니다. 

 

그러나 Java는 어플리케이션이 가동되는 순간에도 모든 상속관계를 점검해서 

 

해당 클래스를 상속받는 클래스들을 다시 컴파일 할 수 있습니다. 

 

이를 Dynamic ClassLoading 기술이라고 하며, Java의 ClassLoader라는 클래스가 제공하는 기능입니다. 

 

이를 통해서 우리는 사용자가 새로 만든 전혀 새로운 클래스라도 다시 컴파일 할 수 있습니다. 

 

다만, 컴파일 해야하는 경로를 명시해 주어야만이 ClassLoader가 이를 찾아갈 수 있습니다. 

 

 

META-INF에 이름을 해당 클래스로 명시한 후, 

 

ServiceLoader에서 현재 로드하고 싶은 SPI (Dictionary) 를 상속한다면 확장 사전 ( SP )가 동적으로 

 

어플리케이션 실행 도중 로드될 수 있는 모든 조건이 갖추어집니다. 

 

 

public class DictionaryServiceExample {

    private static DictionaryServiceExample example;
    
    // ServiceLoader가 Dictionary라는 클래스를 상속받는 클래스를 찾는다. 
   	// .load가 끝나면 사용자가 넣은 새로운 Dictionary (ExtendedDictionary)가 추가된다. 
    
    // ** 이때 내부적으로  META-INF에 있는 파일 이름을 찾아서 해당 경로의 클래스를 로드한다.
    private ServiceLoader<Dictionary> loader = ServiceLoader.load(Dictionary.class);

    private DictionaryServiceExample() {}
    public static synchronized DictionaryServiceExample getInstance() {
        ...// 생략 
    }

    // 2. 모든 사전을 경유하며 단어를 찾는 핵심 서비스 로직이다.
    public String getDefinition(String word) {
        ...// 생략
    }
}

 

 

실제 코드 실행 

 

이제, 이 SPI를 통해서 사용자가 자유롭게 기능을 확장시킬 준비가 거의 끝났습니다. 

 

이제 DictionaryDemo 클래스에서 main 메서드를 통해 실제 사전탐색을 실시해 봅시다. 

 

Jar 파일로 빌드된 ExtendedDictionary의 xml, rest 를 성공적으로 찾아 반환한다면 

 

기존의 코드인 DictionaryService의 변형 없이, 새로운 클래스 코드의 추가 없이, 

 

사용자가 건네준 ExtendedDictionary라는 클래스 구현과 활용에 성공한 것입니다. 

 

public class DictionaryDemo {
    public DictionaryDemo() {
    }

    public void main(String[] args) {
        DictionaryServiceExample dictionary = DictionaryServiceExample.getInstance();
        System.out.println(lookup(dictionary, "xml"));
        System.out.println(lookup(dictionary, "REST"));
    }

    public String lookup(DictionaryServiceExample dictionary, String word) {
        String outputString = word + ": ";
        String definition = dictionary.getDefinition(word);
        return definition == null ? outputString + "Cannot find definition for this word." : outputString + definition;
    }
}

 

 

결론, SPI는 자바의 핵심 기술인 Dynamic ClassLoading 의 실제적 활용이다. 

 

 

이처럼 Java는 런타임 도중에도 클래스를 동적으로 로딩함으로써 

 

클라이언트가 어플리케이션 운영 도중 삽입한 자바 코드에 대해서도 

 

유연하게 바로 반영할 수 있는 장점을 지니고 있습니다. 

 

SPI는 Java의 이러한 Dynamic ClassLoading을 활용한 기술이며 

 

서비스 규모가 확장되어야 할 때 아주 유용한 설계 패턴 중의 하나입니다. 

 

 

기회가 되신다면 소개해 드린 Oracle Tutorial을 직접 따라하시면서 

 

이러한 자바의 강력한 런타임 동적 클래스로딩 기능을 실습해보시기 바랍니다. 

 

다음 글에서는 이러한 Service Provider Framework가 

 

JDBC에서 어떻게 작동하는지를 작성해보도록 하겠습니다. 

 

 

'플라자' 카테고리의 다른 글

JAVA의 객채지향 - 상속 관계  (0) 2022.06.30
JAVA의 객채지향  (0) 2022.06.12

Java 언어는 객체지향 언어로, 현실 사물을 "속성"과 "기능"을 가진 객체로 묘사합니다. 

 

또한 객체간의 관계를 맺어 주어 이해하기 쉽도록 만듭니다. 

 

이 글에서는 Java 언어가 객체간의 "관계"를 설정하는 방법에 대해 다뤄보겠습니다. 

 

 

상속 관계 (Inheritance)

 

두 객체간에 IS-A 관계 (~는 ~이다) 로 이어지는 관계를 보통 상속관계라고 합니다. 


자바에서는 코드의 재사용성, 유지보수, 신뢰성을 유지하기 위해 클래스 간 상속관계를 도입하였습니다. 

 

Java 코드로 묘사한 "물건" 클래스를 통해 상속관계를 이해해봅시다. 

 

“물건을 묘사하는 클래스, 확장가능성이 높은 클래스이다.”

/**
 * 물건을 묘사하는 "객채" 입니다.
 */
public class Thing {

    // 물건은 "모양" 속성을 가집니다.
    String shape;
    // 물건은 "색" 속성을 가집니다.
    String color;
    // 물건은 "주인" 속성을 가집니다.
    String owner;

    public void touchThing() {
        // 누군가가 "만질 수 있는" 기능이 있습니다.
        System.out.println("touch");
    }
    public void useThing() {
        // 누군가가 "사용할 수 있는" 기능이 있습니다.
        System.out.println("use");
    }
}

 

"물건(Thing)"은 우리가 현실에서 여러 가지 사물을 묘사할 때 많이 쓰는 포괄적인 단어입니다. 

 

지시대명사인 "그것", "저것" 처럼 수 많은 사물을 뭉뚱그려 한 단어로 설명할 수 있는 개념이죠. 

 

이렇게 여러 사물에서 공통된 특성을 뽑아내어 더 적은 수의 단어로 묘사하는 것을 "추상화" 라고 합니다.

 

여러 사물의 공통된 속성을 추출하는 것을 추상이라고 한다.

 

 

즉 "사물" 은 자동차, 컴퓨터와 같은 여러 구체적인 것들에서 

 

"물질로 구성되었다"라는 공통된 측면을 뽑아서 한 단어로 묘사한 것입니다.

 

제가 만든 클래스인 Thing은 현실의 단어 "사물"을 바탕으로 묘사한 것이므로 

 

물질로 된 것이라면 무엇이든지 묘사할 수 있습니다.

 

이렇게 많은 개념의 공통된 특성을 하나의 클래스로 묘사한다면 "확장성이 높다" 라고 말 할 수 있겠습니다. 

 

 

실제로 이 Thing을 확장시켜 보겠습니다.  

 

“기계를 묘사하는 클래스, 물건에서 확장된 개념이다. “

/*
* 기계를 묘사하는 "객체"입니다.
* */
public class Machine extends Thing{

    //기계는 "동력원" 을 속성으로 가집니다.
    String energySource;
    // 기계는 "부품"을 속성으로 가집니다.
    String part;

    public void activate() {
        // 기계는 "작동할 수 있는" 기능이 있습니다.
        System.out.println("activated");
    }
    public void shutDown() {
        // 기계는 "멈출 수 있는" 기능이 있습니다.
        System.out.println("SHUTDOWN");
    }

}

 

 맨 윗줄을 보면 class Machine .. 으로, 기계(Machine)이라는 이름의 클래스를 만든 것을 보실 수 있습니다. 

 

그런데 Thing과는 달리, 뒷줄에 두 단어가 더 붙어있습니다. 

 

'extends'와 'Thing'입니다.

 

Thing(사물)을 extends(확장) 시켜서 더 구체적으로 묘사한 결과가 Machine(기계)가 된다는 것입니다. 

 

"기계"는 다른 사물에는 없는 고유한 "속성"과 "기능"도 있습니다. 

 

'돌'은 같은 Thing일지는 몰라도 클래스에 정의된 속성 energySource 와 part를 가질 수는 없습니다. 

 

돌이 activate(작동) 하거나 shutDown(멈춤) 같은 기능이 있다고 보기도 어렵습니다.

 

즉, 여기서의 Machine 클래스는 Thing 클래스가 가진 속성과 기능을 가지면서도 

 

자신의 고유한 속성과 기능을 추가로 가지는 클래스가 된 것입니다. 

 

바로 그렇기 때문에 Thing에서 "확장" 되었다고 말 할 수 있겠습니다. 

 

 

이렇게 Java에서는 A extends B 라는 문법으로 A와 B를 "상속 관계" 로 만듭니다. 

 

두 객체간의 관계를 extends 라는 문법으로 확실히 정의해 주는 것입니다. 

 

객체 A (예를 들어, Thing)은 얼마든지 다른 객체로 "확장될" 수 있습니다.

 

게다가 물건이 꼭 기계만 있는 것은 아니기 때문에

 

Thing extends Cup, 역시 개념적으로 아무런 문제가 없습니다. 

 

객체 A는 무수히 많은 B와 상속관계를 맺을 수 있는 것입니다.

 

“자동차를 묘사하는 클래스, 기계에서 확장된 개념이다.”

/**
 * 자동차를 묘사하는 "객채" 입니다.
 */
public class Car extends Machine{

    // 차는 "엔진 회전수"를 속성으로 가집니다.
    String bpm;
    // 차는 "자동차 바퀴 갯수"를 속성으로 가집니다.
    int numberOfTire;
    // 차는 "주행거리"를 속성으로 가집니다.
    long distanceDriven;

    // 변수가 아닌 Class를 통해서 차와 - 부품들의 Has-a 관계를 묘사합니다.
    // 클래스는 다른 클래스를 "속성"으로 가질 수 (HAS-A) 있습니다.
    CarSheet carSheet = new CarSheet();
    Engine engine = new Engine();
    Wheel wheel = new Wheel();

    public void drive(String owner) {
        //차는 "운전할 수 있는" 기능이 있습니다.
        System.out.println(owner + "drives the car");
    }
    public void useAirConditioner(String bpm) {
        //차는 "에어컨을 돌릴 수 있는" 기능이 있습니다.
        System.out.println("car use AirConditioner with his " + bpm + "bpm Engine");
    }

}

 

이번에는 상속관계에 대해 좀 더 깊이 들어가 보도록 하겠습니다. 

 

현실 자동차의 속성과 기능을 가지는 Car 클래스를 만들어 봅시다. 

 

이번에도 역시 Car extends Machine 이라는 문법을 통해서 Machine을 확장시켜 Car을 묘사하고 있습니다. 

 

그런데, 앞에서 선언했듯이 Machine은 Thing을 확장시켜 만든 클래스입니다. 

 

즉, Car은 Machine을 확장시켜 만들었으니, Thing에도 포함되는 게 확실하겠군요. 

 

이렇게 상속관계는 서로다른 두 객체만 관계가 생기는 것이 아니라, 

 

상속을 받은 ( 확장된 ) 객체가 또 다른 객체와 상속관계를 맺으면 꼬리에 꼬리를 물고 이어지게 됩니다. 

 

결국 Car을 다시 확장해서 벤츠(Benz)를 만들면 "물건(Thing)" 이면서 "기계(Machine)"이면서 "차(Car)"이므로 

 

벤츠는 저 세 가지 객체의 속성과 기능을 모두 가지게 되는 것입니다. 

 

 

 

이런 상속관계를 세밀하게 묘사하면 어떤 장점이 있을까요? 

 

먼저 같은 코드의 재사용성이 높아집니다. 

 

만약 Thing을 상속하지 않았다면, 기계나 자동차에게 "색깔" 속성이나 "만질 수 있는" 기능을 일일히 붙여넣어 주어야 합니다. 

 

그러나 상속관계를 통해 속성과 기능을 공유하도록 만들었기 때문에 그럴 필요가 없습니다. 

 

Thing의 속성과 기능을 정의하는 코드는 앞으로 extends Thing 두 단어만 사용하면 어떤 클래스에든 재사용할 수 있습니다. 

 

코드의 유지보수도 역시 쉬워집니다. 

 

만약 내가 만드는 모든 자동차를 "가솔린" 차량으로 만들고 싶다면, energySource를 검증하는 기능을 하나 만들어주기만 하면 됩니다. 

 

public boolean checkIsGasolineCar() {
   if(energySource.equals("gasoline")) {
        return true; 
    } else {
        return false;
    }
}

 

 

 

게다가 코드에 대한 이해도가 매우 높아집니다.

 

만약 제가 DAS;KJFADFA9124109248S 라는 클래스를 만든다고 가정한다면, 이 코드만으로는 대체 

 

무엇을 묘사하고자 하는지 알기 힘들 것입니다. 

 

그러나 'DAS;KJFADFA9124109248S extends Car ' 라는 문법을 통해 Car 클래스와의 관계를 만들어 준다면 

 

코드를 읽는 사람이 "아 이 DAS;KJFADFA9124109248S는 Car과 관련있거나, 혹은 Car의 한 종류이겠군" 이라고

 

제가 짠 코드의 맥락(Context)을 금방 파악할 수 있을 것입니다. 

 

 

이렇게 상속관계를 세밀하게 묘사하면 객체지향 언어가 가지는 장점인 높은 재사용성, 쉬운 유지보수 뿐만아니라 

 

다른 사람도 이해하기 쉽고, 나중에 자신이 읽어도 금방 기억할 수 있는 코드가 됩니다. 

 

Java 언어를 사용하실 때는 이러한 장점들을 가진 "상속"을 꼭 사용해 보시기를 바랍니다. 

 

 

'플라자' 카테고리의 다른 글

SPI, Service Provider Interface ( 서비스 제공자 인터페이스 )  (0) 2022.07.17
JAVA의 객채지향  (0) 2022.06.12

객채지향 프로그래밍 OOP(Object Oriented Programming)의 정의 


객채지향은 현실을 “객채(Object)”들의 상호작용으로 구현한 프로그래밍 방법론입니다. 

 

객채지향의 장점으로는 세 가지를 꼽을 수 있습니다. 

 

첫째, 재사용성입니다. 

중복된 코드와 개념을 묶어 쉽게 재사용할 수 있습니다. 

 

둘째, 코드관리의 용이성입니다. 

코드변경 시 관리가 쉽습니다. 

 

셋째, 코드의 신뢰성이 높습니다. 

실수할 가능성이 줄어들고, 실수하더라도 빠르게 수정할 수 있습니다. 

 

 

객채(Object) 의 정의 


이러한 장점을 가진 객채지향 방법론으로 우리의 코드를 설계하려면, 가장 중요한 개념인 객체에 대해 알아야 합니다. 

 

객체란, 보통 현실의 사물을 속성(Variable)기능(Method)으로 묘사한 프로그래밍에 쓰이는 가상의 사물입니다. 

 

이 말을 좀 더 쉽게 이해하기 위해서, 자동차에 대한 예시를 통해 현실 사물이 어떻게 객체가 되는지 쉽게 묘사해 봅시다. 

 

"자동차" 라는 물건은 여러 속성(특징)을 가지고 있습니다. 

 

주인이 붙여준 이름이 있을 것이고, 차의 브랜드가 있을 것이고, 차주의 이름, 차의 색깔도 있겠군요. 

 

"자동차"의 기능은 어떤가요? 

 

자동차는 "도로를 달리고", 좋은 자동차라면 "자동주행 기능을 켜고", 브레이크로 "멈춰서고", "주차하고".. 

 

다 나열하기 힘들 정도로 수많은 기능이 있겠군요. 

 

이렇듯이 자동차를 "속성"과 "기능"으로 단순화해서 바라보면 "객채" 가 됩니다. 

 

이런 객채들을 활용하여 프로그램을 만들어 보는 것이 객채지향 프로그래밍입니다. 

 

객채지향 설계의 장점 실습해보기


그렇다면 객체 지향적인 설계와 비객채지향적 설계의 차이점은 무엇일까요? 

 

먼저 자동차를 타고 달리고, 자동주행하고, 멈춰서고, 주차하는 현실의 행위를 Java 코드로 구현해봅시다. 

 

// 서울에서 차를 타고 달리는 상황을 가정하여, 이름을 
// DriveCarInSeoul로 지어봅시다. 
public class DriveCarInSeoul {
 
 	//main이라고 하는 메서드를 통해서 상황을 실행시켜 볼 수 있습니다.
 	public static void main(String[] args) {
    	
            // 자동차의 속성(특징) 묘사하는 변수를 만듭니다.
            // 이름, 브랜드, 주인, 색깔.. 원하는 속성(특징)은 몇 개든 묘사할 수 있습니다!
            String name = "부릉이"; 
            String brand = "현대자동차";
            String owner = "고길동"; 
            String color = "빨간색"; 
            ...

            // 자동차의 기능을 묘사하는 메서드를 만듭니다.
            // 달린다, 자동주행, 멈춰선다, 주차한다, 원하는 기능은 몇 개든 묘사할 수 있습니다!!

            // 앞서 만든 특징들을 활용하여 달리는 상황을 묘사할 수 있습니다. 
            // 어떤 주인이 무슨 브랜드의 차를 모는지를 묘사합니다. 
            System.out.println(owner + "drives" + brand); 


            // 이 기능은 오토파일럿 기능이 실행됨을 묘사합니다.
            System.out.println(name + " autopilot activated"); 

            // 이 기능은 차의 색깔과 차가 멈추었다는 것을 묘사합니다. 
            System.out.println(color + " car stopped"); 


            // 이 기능은 차가 주차되었음을 묘사합니다.
            System.out.println(name + " car parked"); 
    	}
 
}

 

 

이제 여러분은 Java라는 언어로, 서울에서 차를 모는 과정을 멋지게 시뮬레이션 할 수 있습니다!

 

그런데, 차는 서울에서만 몰 수 있을까요? 부산, 강릉, 광주, 춘천.. 어디든 갈 수 있죠.

 

이번에는 부산에서도 차를 몰아봅시다. 역시 차는 달리고, 자동주행하고, 멈춰서고, 주차합니다. 

 

// 이름을 DriveCarInBusan으로 짓는다면 부산에서 차를 모는 과정을 
// 시뮬레이션 할 수 있습니다.

public class DriveCarInBusan {
 
 	//main이라고 하는 메서드를 통해서 상황을 실행시켜 볼 수 있습니다.
 	public static void main(String[] args) {
    	
        	// 자동차의 속성을 먼저 묘사합니다.
            String name = "부릉이"; 
            String brand = "현대자동차";
            String owner = "고길동"; 
            String color = "빨간색"; 
            
            // 이후 자동차가 달리고, 자동주행하고, 멈추고, 주차하는 과정을 묘사합니다.
            System.out.println(owner + "drives" + brand); 
            System.out.println(name + " autopilot activated"); 
            System.out.println(color + " car stopped"); 
            System.out.println(name + " car parked"); 
    	}
 
}

 

아주 간단하게, 우리는 성공적으로 차를 몰고, 부산에서 달리고, 자동주행하고, 멈추고, 주차에 성공했습니다!

 

그런데 하필, 차 주인인 고길동 아저씨가 이번에는 한국의 모든 도시를 돌아보고 싶다고 말하는군요. 

 

시군구의 "시"만  포함하더라도 80개가 넘으니, 80번 넘게 제일 위쪽의 이름을 바꿔  그 안에 같은 코드를 열심히 붙여넣어야 끝납니다.

 

이번에는 80개의 도시를 "파란색" 차로 돌아보고 싶다고 말하는군요. 

 

80개의 도시 이름을 일일히 찾아서, 모든 차의 color 변수를 "파란색" 으로 바꿔줘야 합니다. 

 

심지어 아저씨는 멋지게 "드리프트" 하는 기능도 익혔습니다, 이제 차는 달리고, 자동주행하고, 드리프트하고, 멈추고, 주차합니다! 

 

또 80개를 일일히 찾아서, "드리프트" 기능을 넣어주면 되겠군요. 

 

어찌저찌 80개를 열심히 수정해서 끝냈다고 칩시다, 이제는 해외 도시를 "붕붕이"라는 차로 돌아보고 싶다고 한다면 어떨까요? 

 

해외의 도시는 1만개가 넘으니, "붕붕이"로 바뀐 코드를 열심히 1만번 붙여넣으면 됩니다! 

 

이 다음 한번이라도 더 이름을 바꾼다면 아마 손가락이 먼저 부러질 것 같네요. 

 

 

그럼 우리의 손가락이 부러지기 전에, 이 자동차"속성"과 "기능"을 빼내서 가상의 "객채"로 묶어봅시다.

 

		
        // Car 라고 하는 "객채"로 속성과 기능을 묶었습니다.
        public Class Car() {
        
            // 자동차의 속성을 묘사합니다.
            String name = "부릉이"; 
            String brand = "현대자동차";
            String owner = "고길동"; 
            String color = "빨간색"; 
            
            // 이후 자동차가 달리고, 자동주행하고, 멈추고, 주차하는 과정을 묘사합니다.
            public void driveTheCar() {
            System.out.println(owner + "drives" + brand); 
            System.out.println(name + " autopilot activated"); 
            System.out.println(color + " car stopped"); 
            System.out.println(name + " car parked"); 
            }
    	}

 

이렇게 하면 안에 있는 "속성"과 "기능"을 붙여넣을 필요 없이, 이 "자동차"를 부르기만 하면 됩니다.

 

// 이제 서울부터 뉴욕까지, 같은 자동차로 언제나 달릴 수 있습니다.

// 서울에서,
public class DriveCarInSeoul {
	public static void main(String[] args) {
	Car car = new Car(); 
    car.driveTheCar();
    }
}

// 부산에서, 
public class DriveCarInBusan {
	public static void main(String[] args) {
	Car car = new Car(); 
    car.driveTheCar();
    }
} 
// 인천에서,
public class DriveCarInIncheon {
	public static void main(String[] args) {
	Car car = new Car(); 
    car.driveTheCar();
    }
} 
// 춘천에서,
public class DriveCarInChunCheon {
	public static void main(String[] args) {
	Car car = new Car(); 
    car.driveTheCar();
    }

} 
// 도쿄에서, 
public class DriveCarInTokyo {
	public static void main(String[] args) {
	Car car = new Car(); 
    car.driveTheCar();
    }
} 
// 나가사키에서,
public class DriveCarInNagasaki {
	public static void main(String[] args) {
	Car car = new Car(); 
    car.driveTheCar();
    }
} 
// 뉴욕에서,
public class DriveCarInNewYork {
	public static void main(String[] args) {
	Car car = new Car(); 
    car.driveTheCar();
    }
}

... (생략)

 

이제 훨씬 더 간결한 코드 몇줄만으로도 우리가 만든 멋진 차의 "속성"과 "기능"을 그대로 다시 사용할 수 있습니다.

 

이처럼 객채지향적 코드는 재사용성이 뛰어납니다.

 

또, 차의 색깔이 바뀌더라도 1만번 다시 붙여넣는 대신 단 한번, Car이라는 객체의 color를 바꿔주면 됩니다. 

 

코드를 고치거나 바꾸는 유지보수도 용이합니다.

 

졸다가 두 번 붙여넣는다 해도 걱정없습니다, 1만 개의 코드를 다 점검할 필요 없이 하나의 "객체"에 두 번 들어갔는지만 확인하면 됩니다. 

 

오탈자의 위험성도 낮아지고, 구현된 기능에 대한 신뢰성이 높은 코드가 되었습니다. 

 

 

 

이처럼 객체지향 언어는 그 뛰어난 재사용성, 유지보수 용이성, 신뢰성으로 현재 널리 쓰이는 대표적인 프로그래밍 언어가 되었습니다. 

 

Java 코드를 해석하고 구현할 때, 이 세 가지 장점들을 고려하면서 구현한다면 

 

훨씬 빠르고, 쉽게 고칠 수 있고, 믿음직한 코드가 될 것입니다. 

 

Java 언어로 코드를 구현할 때는 이런 객채지향 언어의 장점을 고려하여 자신의 코드를 구현해 보시기를 바랍니다.

 

+ Recent posts