하마
7k
2016-08-07 16:59:49
0
4336

스칼라 강좌 (11) - getter 와 setter


Getter 와 Setter 


객체지향 프로그래밍에서 게터와 세터는 의도치 않게 이제 기본이 된  내용들 중 하나이지만 ( getter / setter 자체를 그냥 public 으로 변수 선언하는것과 마찬가지로 나쁘게 보는 시각도 있습니다. 객체지향은 외부 노출을 줄여야 한다고 보는데 게터,세터는 절차지향 마인드의 산물 ) 때로는 쓰기 귀찮아질 때 도 있긴합니다. 대부분의 게터와 세터는 매우 비슷하기 때문에 같은 기능을 하는 더 나은 방법이 있을거란 생각은 매우 타당 할 것이며 C# 에서는 그것을 위해 특별히 "프로퍼티"라는 것을 만들어서  아래와 같이 사용됩니다.

    private String strName;
 
    public String StrName
    {
        get { return this.strName; }
        set 
        {
               ... 제어 .. 
              this.strName = value; 
        }
    }

자바에서는 언어차원에서 편하게 쓰게 하기위한 장치는 없고(Java 1.7 에 @property  같은거 만들거란 말은 있었는데 안한듯? 그러고 보면 자바는 멀티라인 문자열 같은 엄청 불편한 것 들을  왜 개선안하는지..)  IDE 차원이나 Lombok 같은 툴의 도움을 받습니다. 

관련 읽을 거리:

왜 자바에서 getter / setter 는 악마인가 (아래 한글 서적 참고)

* 왜 getter / setter 를 사용하는가 (스택오버플로우)

* 정보은닉과 캡슐화의 차이  (한글블로그)

* Anemic domain model 안티패턴 (마틴파울러)

* 홀럽 아저씨의 실용주의 디자인패턴 책 (한글) 에  왜 getter/setter 가 왜  안 좋은지 자세히  나온다.

이 책에선 게터,세터는 객체지향과 거리가 있으며  데이터의 흐름이 적을 수록 유지보수에 좋다라고 말 하고 있습니다. pull 해서 쓰지 말고 push 해서 쓰라는 지침. 즉 가져와서 하지 말고 맡겨라!  데이터베이스와 접점 부분등 몇몇 예외상황에 대해서도 말 합니다. 


스칼라에서의  Getter / Setter 

다음 첫번째 예를 보면 :  

class Person() {  
 var name = "" 
 var age = 0 
} 

첫눈에 보기에 이 클래스가 단지 사람 객체를 만들고 그 이상 아무것도 없어 보이겠지만 
사실 인스턴스화 되면 다음과 같이 get / set 을 할 수 있게 됩니다.

GET 

//person 객체 만들고 
person = new Person() 

// age 과 name 속성을 출력한다. 
println(person.age)  
println(person.name)  

SET

// Set the properties to different values 
person.age = 34  
person.name = "Dustin Martin"  

매우 간단한데  이 예를 보면 이렇게 생각 하실 듯 합니다.  이것은  getters 와 setters 가 아니잖아! 


자동으로 생성되는 Getter / Setter 

스칼라에서는 어떤 객체의 멤버 중 비공개가 아닌 모든 var 멤버에 게터와 세터 메소드를 자동으로 정의해 줍니다. 이때 자동으로 정의해 주는 이름은 자바의 관례와 다르며 age 의 게터는 그냥 age 이고 세터는 age_= 입니다. 이때 필드에는 자동적으로 private[this] 가 붙는데 그 필드를 포함하는 객체에서만 접근할 수 있다는 의미이고 자동으로 만들어진 게터와 세터는 원래의 age 와 같은 가시성이됩니다. 즉 age 가 공개이기 때문에 공개가 된다는 뜻 


 자신만의 Getter / Setter  정의 하기 - (1)

여기서 만약 age 속성에 0살~200살까지의 범위를 확인하는 절차가 필요할때 어떤 일이 발생 할까? 
name 에 특정한 방식으로 포맷을 정하고 싶을때 어떻게 할까?  

스칼라에서 다루는 방법을 알아보겠습니다.

class Person() {  
 // age 속성을 private 로 바꾸고 , _age 으로 이름을 바꾸자.  
 private var _age = 0 
 var name = "" 

 // Getter 
 def age = _age 

 // Setter 
 def age_= (value:Int):Unit = _age = value 
} 

매우 간단한 코드인데. 첫번째로 "age" 를 "_age" 로 이름을 바꾸고 private 를 적용하였습니다. 

GET 

def age = _age  

변수를 리턴하는 age 메소드를 정의하였습니다.  스칼라는 return 키워드를 요구하지 않습니다. 

또한 Scala 는 메소드의 몸체에 괄호를 사용하는것이 의무가 아닙니다.

SET

다음엔 setter 를 살펴보죠.

def age_= (value:Int):Unit = _age = value  

-  메소드 이름은  "age_="  입니다. 
- :Unit 는 void 를 리턴하다는 것과 동등하며 생략가능합니다.  
- priavate 변수인 _age 에 새 값인 value 를 대입한다는 뜻입니다.

아직까지 먼가 제한을 두는 코드가 들어 있지는 않았습니다. 이제 제한을 넣어 보죠. 


 자신만의 Getter / Setter  정의 하기 - (2)

class Time {

 private [this] var h = 12

 private [this] var m = 0

 def hour : Int = h  // getter

 def hour_= (x : Int) { //setter

   require (0 <= x && x < 24) 

   h = x

 }


 def minute = m  // getter

 def minute_= (x: Int) {  // setter 

   require(0 <= x && x < 60)

   m=x

 }
}
 이제 Set 을 할 때 값을 점검합니다. 여기서는 시간과 분이 특정 값 내에 있어야한다고 제한하고 있네요.

 

자신만의 Getter / Setter  정의 하기 - (3)

class Termometer {

   var celsius : Float = _   // celsius 의 getter/setter 는 디폴트구현을 사용

   def fahrenheit = celsius * 9 / 5 + 32  //fahrenheit 는 메소드만 존재해도 됨

   def fahrenheit_= (f : Float) {    

     celsius = ( f - 32 ) * 5 / 9

   }

}
--------------------------------------------

var t  = new Termometer

t.celsius = 100

t.fahrenheit = -40

 fahrenheit 같이 변수를 명시적으로 선언하지 않고도 getter / setter 로 사용할 수 있습니다.

celsius 를 var 로 정의하며 초기화 값으로 _ 를 주었는데 이것은 디폴트값으로 영을 할당합니다.
영은 숫자에서는 0 이고 불리언에서는 false 이며 레퍼런스에서는 null 입니다자바에서 초기화 하지 않았을때 할당되는 동일한 규칙 이며 스칼라에서는 초기화를 생략할 수 없습니다. 만약 초기화 하지 않는다면 그건 추상변수를 선언해버립니다.  추상변수는 다음에 알아보시죠.

@scala.reflect.BeanProperty  애노테이션 

위에 보다시피 보통 스칼라 코드에서는 명시적인 필드 get,set 메소드가 필요 없습니다. 그냥 디폴트 구현을 사용하다가 제한이 필요하거나 할때 재정의해서 사용할 수 가 있게 되죠. 그때 모든 코드들을 찾아다니며 변경할 필요가 없게 됩니다.  하지만 플랫폼에 따라 몇몇 프레임워크는 get,set 메소드를 꼭 필요로 하는 경우가 있기때문에 이를 위해 스칼라는 @scala.reflect.BeanProperty 애노테이션을 제공합니다.

필드에 이 애노데이션을 추가하면, 컴파일러가 자동으로 get,set 메소드를 만들어 줍니다.  예를 들어 crazy 필드를 애노테이션하면, 자바처럼  get메소드는 getCrazy가, set 메소드는 setCrazy 가 됩니다.


1
  • 댓글 0

  • 로그인을 하시면 댓글을 등록할 수 있습니다.