2016/03/02 - [java] - 추상화(Abstraction) 이해의 중요성 #1


자동차는 영어로 Car이고, 자동차를 이루는 구성요소로는 엔진(Engine), 바퀴(Wheel), 제조사(Manufacturer), 모델(Model) 등이 있습니다. 중고매매에서 가장 중요한 주행거리, 연식, 자동차 색상도 놓칠순 없겠군요. 단순한 자동차(Car) 추상화는 java.lang.Object만을 상속하는 Car 클래스를 만드는 것입니다. 다른 말로 POJO(Plain Old Java Object)를 만드는 것입니다.

자동차 추상(Abstraction)은 com.jayjaylab.app.usedcar.Car 클래스 아래와 같이 만들었습니다.

package com.jayjaylab.app.usedcar

public class Car {
    public int drivingDistance;    // in km(killo meters)
    public String dateMadeAt;
    public String color;
    public String engine;
    public String wheel;
    public String manufacturer;
    public String model;
    public String trunk;
    public String roof;
    // and so on...
} 

자동차는 제조사에서 한번 만들어지면 제조사, 모델이 변경되지 않습니다. 위처럼 자동차를 단순하게 추상화했는데, 자동차에서 자주 변경이 되는 부분, 변할 수 있는 부분이 무엇이 있을까 생각해 봅시다.

  • 엔진이 바뀔 수 있다.
  • 휠이 바뀔 수 있다.
  • 주행 거리가 바뀔 수 있다. 자동차 중고매매 사업장에서 이런 요구사항이 있다면 주행 거리 조작! 불법이지요;;
  • 루프가 바뀔 수 있다.

상기 정도로 정리해 볼 수 있습니다. 위 Car 클래스가 이런 변경 사항에 충분히 유연한지 한번 점검해 보겠습니다.

package com.jayjaylab.app.usedcar

public class Car {
    public int drivingDistance;    // in km(killo meters)
    public String dateMadeAt;
    public String color;
    public String engine;
    public String wheel;
    public String manufacturer;
    public String model;
    public String trunk;
    public String roof;
    // and so on...

    public static void main(String[] args) {
        Car car = new Car();
        // 엔진 변경
        car.engine = "yamato";
        car.engine = "superengine";
        // 휠 변경
        car.wheel = "kumho_wheel";
        car.wheel = "fantastic_wheel";
        // 루프 변경
        car.roof = "default_roof";
        car.roof = "sun_roof";
        // 주행 거리 변경. 불법 이죠잉!
        car.drivingDistance = 100;  // 100 km
        car.drivingDistance = 200; // 200 km
    }
} 

위 소스 코드를 보니, 런타임 동안 자동차 엔진, 휠, 루프, 주행거리 변경을 손 쉽게 할 수 있습니다. 그런데 엔진, 휠, 루프, 주행거리를 변경하는데 Car 클래스의 멤버변수를 직접 접근해서 변경해야 합니다. Car 클래스를 사용하는 입장에서 Car 클래스의 멤버 변수가 무엇인지 알 필요가 없습니다. 엔진을 변경하기 위해 Car 클래스에서는 Car.engine이라는 멤버 변수를 외부에 노출시키는게 아닌 엔진을 변경할 수 있는 메소드를 제공해야할 것입니다. Car 클래스 사용자들(개발자들)에 Car 클래스의 속성을 변경 시키려면 메소드를 통하여 변경 시키도록 하고 해당 멤버 변수는 개발자에게 노출시키지 않도록 합니다. 위의 Car 클래스는 캡슐화(Encapsulation)이 잘못된 예의 하나입니다. 아래와 같이 Accessor를 public에서 protected로 변경하도록 하고 멤버 변수에 대한 직접 접근을 막습니다. 사실 멤버 변수를 직접 참조하는것이 메소드를 호출하는 것보다 몇 CPU 싸이클 빠를것 같지만 난독화 때문에 Proguard 사용하면 신경 안써도 됩니다. getter-setter inlining을 proguard가 해준다고 하네요. [참고 http://developer.android.com/training/articles/perf-tips.html#GettersSetters]

package com.jayjaylab.app.usedcar

public class Car {
    int drivingDistance;    // in km(killo meters)
    String dateMadeAt;
    String color;
    String engine;
    String wheel;
    String manufacturer;
    String model;
    String trunk;
    String roof;
    // and so on...

    public void setEngine(String engine) {
        this.engine = engine;
    }

    public void setWheel(String wheel) {
        this.wheel = wheel;
    }

    public void setRoof(String Roof) {
        this.roof = roof;
    }
    
    public void setDrivingDistance(int drivingDistance) {
        this.drivingDistance = drivingDistance;
    }

    ...

    public static void main(String[] args) {
        Car car = new Car();
        // 엔진 변경
        car.setEngine("yamato");
        car.setEngine("superengine");
        // 휠 변경
        car.setWheel("kumho_wheel");
        car.setWheel("fantastic_wheel");
        // 루프 변경
        car.setRoof("default_roof");
        car.setRoof("sun_roof");
        // 주행 거리 변경. 불법 이죠잉!
        car.setDrivingDistance(100);  // 100 km
        car.setDrivingDistance(200); // 200 km
    }
} 

Car 클래스 사용자가 엔진, 휠 등을 변경하기 위해서 위 처럼 set method를 사용하도록 강제하면 Car의 멤버 변수를 잘못된 값으로 변경하는 것을 막을 수 있습니다. 예를 들면, Car.setEngine(), Car.setWheel()의 메소드에 매개변수가 유효한지 체크하는 로직을 넣거나 현재 변경 가능한 상태에만 변경을 하도록 로직을 추가하여 각 변경을 안전하게 수행하도록 합니다. 또한 메소드를 통하여 변경을 할 경우 멀티 쓰레딩 환경에서 thread safety 하도록 로직을 추가할 수도 있습니다. 응용 프로그램을 개발할 때 절대 네버 멤버 변수를 public로 선언하여 직접 접근하는 방식을 피하셔야 합니다. getter-setter로 Encapsulation 하시는 것은 기본입니다. 예외인 경우는 자료 구조 만들 때인데요. 성능(메소드 사용으로 인한 스택 접근 시간)을 위해 cpu cycle 사용을 최소화하기 위한 노력을 자료 구조에서는 해줘야하는데, 자료 구조 내부에서 getter-setter를 이용하지 않고 멤버 변수에 직접 접근을 하는 경우가 있습니다. 안드로이드의 경우는 ViewHolder 패턴을 사용할 때 멤버 변수를 직접 접근합니다. ViewHolder 클래스 내부 멤버변수를 접근할 때 getter로 접근하지 않고 멤버 변수를 직접 접근해서 UI thread 사용 시간을 최소화 해줘야 합니다.


잡설이 많았는데, 그럼 다음 글에서 더욱 제대로된 Car abstraction을 만들어 봅시다.

Posted by 제이제이랩
,