ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 2022년 7월 9일 TIL - 멤버 변수의 가시성과 접근 방식에 대한 고찰
    Today I Learned 2022. 7. 9. 23:30

     

    프로그래밍을 하면서 거의 무조건 이런 방식으로 써야 한다고 해서 그렇게 써오고 있던 규칙 같은 것들이 있었다. 대표적으로 객체를 정의할 때 객체의 데이터인 멤버 변수의 접근 제어자는 private으로, 객체의 동작인 메서드의 접근 제어자는 public으로 지정하여 멤버 변수에는 메서드를 통해서만 접근하도록 하는 등이다. 사실 멤버 변수의 접근 제어자를 public으로 지정한다고 해서 문법적으로 틀려서 컴파일 에러가 발생한다던지 하지는 않는다. 그러나 이를 지키지 않으면 원칙에 위배되는 것이므로 그렇게 해서는 안 된다고 하길래 계속 그렇게 써 오고는 있었는데, '그게 그렇게까지 문제될 일인가?'라는 생각은 남아 있었다.

     

    어제 트레이너님과 이야기를 나누면서 멤버 변수가 private이어야 하는 간략한 이유를 들어볼 기회가 있었고, 이해한 내용을 간단한 소스코드를 들면서 정리해보고자 한다.

     

     

     

    다음과 같이 Bike 클래스를 정의했다. 멤버 변수로 하나의 String 문자열 변수만을 갖고 있는 간단한 클래스이다.

    public class Bike {
      public String color;
    
      public Bike(String color) {
        this.color = color;
      }

     

    다음과 같이 main 메서드에서 해당 클래스의 인스턴스를 생성한 뒤 해당 인스턴스의 데이터에 접근하는 작업을 수행했다.

    public class EncapsulationTest {
      public static void main(String[] args) {
        Bike bike = new Bike("red");
    
        System.out.println("The color of this bicycle is " + bike.color + ".");
    
        bike.color = "yellow";
    
        System.out.println("The color of this bicycle is " + bike.color + ".");
      }
    }
    
    //출력
    //The color of this bicycle is red.
    //The color of this bicycle is yellow.

     

    위의 소스코드에서 bike 객체를 조작하는 방식의 문제점은, bike 객체의 데이터를 조작하는 데 bike 객체 자신이 전혀 관여할 수 없다는 데 있다. 만약 bike의 color에 할당되어서는 안 되는 값이 있다고 하더라도 얼마든지 bike를 조작하는 위치에서 할당되어서는 안되는 값을 할당해버릴 수 있다. 이로 인해 서로 다른 객체들이 소스코드 내에서 가져야 하는 책임의 범위가 모호해지고, 객체의 데이터에 대해 안정성을 보장할 수 없게 된다.

     

     

    이번에는 Bike 클래스의 멤버 변수 color의 접근 제어자를 private으로 변경한 뒤, main 메서드에서 다시 생성한 인스턴스의 데이터에 접근하는 작업을 수행했다. Bike 객체의 color에 "yellow"는 할당될 수 없도록 Bike 클래스를 정의하였다.

    public class Bike {
      private String color;
    
      public Bike(String color) {
        this.color = color;
      }
    
      public String color() {
        return this.color;
      }
    
      public void setColor(String color) {
        if (color.equals("yellow")) {
          System.out.println("Yellow is not allowed.");
    
          return;
        }
    
        this.color = color;
      }
    }
    
    public class EncapsulationTest {
      public static void main(String[] args) {
        Bike bike = new Bike("red");
    
        System.out.println("The color of this bicycle is " + bike.color() + ".");
    
        bike.color = "yellow";    //error
        bike.setColor("yellow");
    
        System.out.println("The color of this bicycle is " + bike.color() + ".");
    
        bike.setColor("green");
    
        System.out.println("The color of this bicycle is " + bike.color() + ".");
      }
    }
    
    //출력
    //The color of this bicycle is red.
    //Yellow is not allowed.
    //The color of this bicycle is red.
    //The color of this bicycle is green.

     

    해당 구조에서는 더이상 객체 외부에서 객체의 데이터를 임의로 조작할 수 없게 되었다. 객체 외부에서 객체의 데이터에 접근하기 위해서는 객체의 메서드를 통해야 하기 때문에 객체의 데이터의 안정성이 좀 더 보장되게 되었다.

     

    다만 위의 구조도 아직까지는 완전한 안정성이 보장된다고 보기에는 아쉬운 부분이 있다. 위의 구조에서는 여전히 객체의  데이터의 값을 어떤 값으로 변경하도록 할지에 대한 책임이 객체 외부에 부여되어 있기 때문에 객체 내부에서 제한 조건을 온전히 설정하지 못할 경우에는 여전히 안정성에 문제가 발생할 수 있다.

     

    이를테면 다음과 같은 문제가 발생할 수 있다.

        bike.setColor("transparent color");
    
        System.out.println("The color of this bicycle is " + bike.color() + ".");
      }
    }
    
    //출력
    //The color of this bicycle is transparent color.

    (투명한 자전거라니... 도로를 활개치게 냅둔다면 교통사고가 발생헐 가능성이 매우 높을 것이다.)

     

     

    이번에는 다음과 같이 구조를 변경해 보았다.

    public class Bike {
      private String color;
    
      public Bike(String color) {
        this.color = color;
      }
    
      public String color() {
        return this.color;
      }
      
      public void setColorToRed() {
        this.color = "red";
      }
    
      public void setColorToGreen() {
        this.color = "green";
      }
    
      public void setColorToBlue() {
        this.color = "blue";
      }
    }
    
    public class EncapsulationTest {
      public static void main(String[] args) {
        Bike bike = new Bike("red");
    
        System.out.println("The color of this bicycle is " + bike.color() + ".");
    
        bike.setColorToGreen();
    
        System.out.println("The color of this bicycle is " + bike.color() + ".");
    
        bike.setColorToBlue();
    
        System.out.println("The color of this bicycle is " + bike.color() + ".");
      }
    }
    
    //출력
    //The color of this bicycle is red.
    //The color of this bicycle is green.
    //The color of this bicycle is blue.

     

    해당 구조에서는 객체의 상태 변경을 객체에 지정된 특정 메서드만을 통해 수행하도록 하고, 데이터에 설정할 수 있는 값의 범위를 객체가 완전히 통제하고 있다. 따라서 객체 외부에서는 객체의 데이터를 변경하기 위해 객체의 정해진 메서드만을 호출하고, 데이터의 변경은 객체 자신이 온전히 수행함으로써 각 객체 간의 책임 분리가 명확해졌고, 객체의 데이터에 입력되는 값을 객체에서 통제할 수 있게 되어 데이터의 안정성이 보장되게 되었다.

     

     

     

    지금까지의 내용을 정리해보면 다음과 같다. 

    • 멤버 변수의 가시성은 예상 가능한 범위 내에서의 안정성 보장을 위해 private이어야 한다.
    • 어떤 객체와 그 객체의 메서드를 호출하는 객체 외부 간의 책임을 명확히 나누기 위해 메서드의 호출 방식이 객체 외부에 덜 의존적이게끔 설계될 필요가 있다.

     

    애매하게 의문점으로 남아있었던 부분을 트레이너님께 조언을 듣고 정리함으로써 그 이유와 의미를 명확히 가져가는 시간을 가질 수 있었다. 트레이너님의 조언을 지속적으로 기억하고 내 것으로 만들 수 있도록 오늘 정리한 내용처럼 트레이너님께 조언을 듣고 나면 들은 것들을 지속적으로 정리해나가면서 이해할 수 있도록 노력해야 할 것이다.

     

     

     

    댓글

Designed by Tistory.