음 제목 만 보면 조금 문장이 햇갈릴수도 있다.
풀어서 이야기하면 필드(맴버변수)를 public 으로 선언하지말고 접근자 메소드를 만들어라. 라는거다.
우리는 보통 C 나 C++ 을 기본적으로 배운다.
일반적으로 처음에는 변수, 함수 에대해서 배우고 구조체, 포인터 에 대해서 배우게 될것이다. 이때까지만 해도 접근 제한자 라는 놈은 없었다. C++에서도 클레스를 사용하고 java를 배우기 시작하면서 접근 제한자 라는놈을 만나게 되는데 처음 객체지향 프로그래밍을 접하게 되면 닥치고 public 혹은 아무것도 안적거나 를 시전하게된다.
이것은 이전 항목에서도 설명한 캡슐화라는 개념에서 상당히 위험한 요소이다.
솔직히 이런부분은 써보면 안다. 라고 말하는게 설명하는 입장에서 가장 쉬운 설명인데 좀 이야기를 풀어보도록 하자.
일단 가장 쉽게 Point 라는 x,y 좌표를 표시하는 클레스를 만들었다고 하자.
class Point{ public int x; public int y; public Point(int x, int y) { this.x = x; this.y = y; } }
이렇게 만들었다고 해보자 그냥 x와 y좌표를 바꾸기 위해서는 Point.x=10, point.y=20 이런식으로 해주면된다.
근데 이게 위험하니까
class Point{ private int x; private int y; public Point(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } }
이렇게하자? 뭐야 이 개떡같이 길기만하고 의미 없는 코드들은?
이라는 감상평을 들을수 있겠다. 솔직히 개인적으로도 뭐하려고 저딴짓을 하나 싶기도 하다. 근데 우리가 저걸 더 확장해서 Vector 이라는 클레스를 만들어 보도록 하자.
Vector은 방향과 크기가 있다. 라는것을 익히 알것이다. (모른다면 한번쯤 찾아보는것도 좋을것이다. 그래픽 관련된 프로그래밍 할때 종종 쓰인다.)
class Vector{ public double x; public double y; public double angle; public double value; public Vector(double x, double y) { this.x = x; this.y = y; angle=Math.atan2(x, y); value=Math.sqrt(x*x+y*y); } }
벡터에 대해서 클레스를 만들었는데 여기에는 x,y 를 표시하는 방향, 크기 와 그것을 계산해서 원점으로부터 각도와 크기를 구하고자 한다. 그러면 몇몇의 연산이 좀더 들어가야된다.
x,y 를 이용해서 각과 크기를 구해서 angle과 value라는 변수에 저장을 했다. 이것을 이용해서 다음과 같은 코드를 짜보았습니다.
class Vector { public double x; public double y; public double angle; public double value; public Vector(double x, double y) { this.x = x; this.y = y; angle = Math.atan2(x, y); value = Math.sqrt(x * x + y * y); } @Override public String toString() { // TODO Auto-generated method stub return String.format("x : %f, y : %f, angle : %f, value : %f",x,y,angle,value); } public static void main(String[] args) { Vector v=new Vector(1, 1); System.out.println(v); v.x=2; System.out.println(v); } }
x : 1.000000, y : 1.000000, angle : 0.785398, value : 1.414214
x : 2.000000, y : 1.000000, angle : 0.785398, value : 1.414214
결과는 이렇게 나옵니다. x : 2.000000, y : 1.000000, angle : 0.785398, value : 1.414214
이해가 잘안되는 분을 위해 좀더 설명하겠습니다.
처음에 x=1,y=1 이용해서 객체를 생성하면 각은 45도 즉 pi/4 의 값을 가지고 크기는 루트2 입니다.
근데 여기서 x를 2로 바꾸었다면 x=2,y=1 이기 때문에 각은 대충 25도쯤 크기는 루트3 이 됩니다.
이게 제가 보여드릴수 있는 가장 쉬운 예입니다. 그렇다면 이것을 어떻게 처리해야 하느냐?
class Vector { private double x; private double y; private double angle; private double value; public Vector(double x, double y) { this.x = x; this.y = y; angle = Math.atan2(y, x); value = Math.sqrt(x * x + y * y); } @Override public String toString() { // TODO Auto-generated method stub return String.format("x : %f, y : %f, angle : %f, value : %f",x,y,angle,value); } public static void main(String[] args) { Vector v=new Vector(1, 1); System.out.println(v); v.setX(2); System.out.println(v); } /** * x,y 를 angle와 value를 이용해서 다시 계산한다. */ private void calcXY(){ x=value*Math.cos(angle); y=value*Math.sin(angle); } /** * angle, value 를 x와 y를 이용해서 다시 계산한다. */ private void calcAV(){ angle = Math.atan2(x, y); value = Math.sqrt(x * x + y * y); } public double getX() { return x; } public void setX(double x) { this.x = x; calcAV(); } public double getY() { return y; } public void setY(double y) { this.y = y; calcAV(); } public double getAngle() { return angle; } public void setAngle(double angle) { this.angle = angle; calcXY(); } public double getValue() { return value; } public void setValue(double value) { this.value = value; calcXY(); } }이렇게 바꾸면 됩니다.
일단 코드가 무지막지하기 길어졌습니다. 이때문에 이런걸 싫어하는 사람도 있습니다만은..
하여튼 이렇게 코드가 완성되면 x와 y, angle, value 가 각각 수정될때마다 영향을 받는 다른 값들을 동시에 수정을 하고 외부에서 그 값을 각각의 값들이 말도 안되는 형태를 취하고 있는것을 피할수 있게됩니다.
x : 1.000000, y : 1.000000, angle : 0.785398, value : 1.414214
x : 2.000000, y : 1.000000, angle : 0.463648, value : 2.236068
실행해보면 이렇게 제대로된 결과를 뽑아줍니다.x : 2.000000, y : 1.000000, angle : 0.463648, value : 2.236068
추가로 뭘 변경 하면 좋을까요. angle 이 조금 마음에 안드네요. 우리가 보기 좋은것은 각도 로 표시되는거지 라디안이 아니거든요.
이때 각도용 set을 하나 만들어줍니다. 그리고 toString 도 좀 수정하면 좋겠네요.
//추가 public void setDegree(double degree){ setAngle(Math.toRadians(degree)); } //수정 public String toString() { return String.format("x : %f, y : %f, angle : %f, value : %f",x,y,Math.toDegrees(angle),value); }
public static void main(String[] args) { Vector v=new Vector(1, 1); System.out.println(v); v.setX(2); System.out.println(v); v.setDegree(60); v.setValue(2); System.out.println(v); }
그리고 원래 만들었던 main 을 이렇게 바꾸어 보았습니다.
x : 1.000000, y : 1.000000, angle : 45.000000, value : 1.414214
x : 2.000000, y : 1.000000, angle : 26.565051, value : 2.236068
x : 1.000000, y : 1.732051, angle : 60.000000, value : 2.000000
x : 2.000000, y : 1.000000, angle : 26.565051, value : 2.236068
x : 1.000000, y : 1.732051, angle : 60.000000, value : 2.000000
출력은 이렇게 나옵니다.
하여튼 여러모로 접근자 메소드(setter, getter)로 클레스를 구성해놓으면 나중이 편합니다.
추가. Getter,Setter 만드는 방법1
아니면 코드 중간에 set 를 적은뒤 컨트롤+스페이스, get 적은뒤 컨트롤+스페이스 를 하게되면
'Effective Java > Chapter 4' 카테고리의 다른 글
항목 18. 추상 클레스보다는 인터페이스를 상용하자. (0) | 2012.01.02 |
---|---|
항목 17. 상속을 위한 설계와 문서화를 하자. 그렇지 않면 상속의 사용을 금지시킨다. (0) | 2011.12.29 |
항목 16. 가급적 상속보다는 컴포지션을 사용하자. (0) | 2011.12.29 |
항목 15. 가변성을 최소화 하자. (0) | 2011.12.26 |
항목 13. 클래스와 그 맴버의 접근성을 최소화하자. (0) | 2011.12.17 |