개체는 자신의 상태를 스스로 책임져야 함!
- 즉, 개체 외부에서 개체의 상태에 직접 접근하는 것을 막아야 함
- 개체의 상태를 변경하는 주체는 개체 자신인 게 이상적
- 접근 제어자(access modifiedr)를 통해 이런 일을 할 수 있음
접근 제어자
- 어떤 외부자들이 개체 속에 접근할 수 있는지 정의
- public: 누구나 접근 가능
- protected: 자식들만 접근 가능
- 생략할 경우: 같은 패키지에 속한 클래스들만 접근 가능
- default 혹은 package 접근 제어자라고 부름
- private: 외부 접근 금지
접근 제어자: private
- 외부자들은 접근할 수 없음
- 클래스 내부에서만 접근 가능함
- 클래스의 경우 내포(nested) 클래스에 한 해 붙일 수 있음
private 멤버 변수
public class Human {
private String name;
private int age;
private Sex sex;
private Citizenship citizenship;
public void walk(){
this.age += 1; // ok
}
// ...
}
Human adam = new Human("Adam", 20, Sex.MALE, Citizenship.KOREA);
adam.age = 0; // compile error
일반적인 접근 제어자
- 보통 다음과 같은 접근 제어자를 사용
- 멤버 변수: private(또는 protected)
- 메서드: public
- 멤버 변수 접근은 메서드를 통해서만!
- 캡슐화: 외부에서 캡슐을 뚫고 들어오지 못함.
- 추상화: 속에 데이터가 무엇이 있는지 밖에서는 전혀 모름. 추측만 가능.
private 메서드는 클래스 안에서만 호출할 수 있음.
고로 클래스 안에서 중복되는 코드를 막는데 사용
private과 생성자
- 생성자도 멤버 함수이므로 똑같은 규칙 적용
- new를 못하니 적어도 생성자 하나는 public인 것이 보통
private 접근 제어자
- 외부에서 접근을 시도하면 컴파일 오류
- 내부에서는 접근 가능
내부란 클래스 내부를 의미!
클래스 내부 != 개체 내부
같은 클래스에 속한 개체끼리는 private 멤버에 접근 가능
public class Human{
private int age;
// ...
public void punch(Human enemy){
enemy.age -= 1; // ok
this.age += 2; // ok
}
}
Human adam = new Human(); // age: 20
Human james = new Human(); // age: 30
adam.punch(james); // adam age : 22
// james age : 29
접근 제어자를 안 붙일 경우
- 기본(defalut) 또는 패키지(package) 접근 권한
- 같은 패키지 안에 있는 클래스끼리 서로 접근 가능
- 같은 패키지: public처럼 작동
- 다른 패키지: private처럼 작동
- OOP에서 흔히 말하는 접근 제어자는 아님
- C#에서도 internal이라는 비슷한 접근 제어자가 있음
패키지 접근 제어자 용도
- public 대신 패키지 접근 제어자를 사용할 수 있다면 그리할 것
- 어떤 패키지 안에서만 사용되는 클래스
- 'public이 아닌 내포 클래스'를 최상위 클래스로 바꿀 때 쓸 것
- 내포 클래스는 가독성 문제가 생길 수 있음
- 따라서 별도의 클래스로 분리시키는 것이 요즘 트렌드
- 이 때 접근 권한을 패키지 내로 제한하는 것이 public보다 나음
getter / setter
public class Human {
private String name;
// ...
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
// ...
}
개체 스스로 자신을 책임지도록 하는 게 OOP의 정신
그 클래스를 작성한 프로그래머가 그 클래스에 대해 가장 잘 안다
함수를 통한 데이터 접근의 객관적인 장점
- 멤버 변수를 저장하지 않고 필요할 때마다 geter에서 계산 가능
- 예: 질량과 중력 멤버 변수로부터 무게를 계산
- setter에서 추가적인 로직을 실행할 수 있음
- 예: 음수의 나이가 인자로 들어올 경우 무시
- 상속을 통한 다형성 구현 가능
베스트 프랙티스 1: 멤버 함수는 private
- 대부분의 경우 모두 private
- 정보 숨기기(informateion hiding)
베스트 프랙티스 2: 새 개체는 유효하도록
- 개체는 살아있는 동안 언제나 유효한 상태여야 이상적
- 처음 생성될 때도 마찬가지
- 그래야 실수를 막을 수 있음
- 생성자를 통해 이를 강제할 수 있음
- 매개변수가 틀릴 경우 컴파일 자체가 안 됨
- 매개변수가 바뀌는 경우도 마찬가지
public Human(String name, Sex sex) { ... }
private Human(String name, int age, Sex sex, Citizenship citizenship) { ... }
Human adam = new Human(); // compile error
Human james = new Human("James", 31, Sex.MALE) // compile error
Human gabriela = new Human("Gabriela", 39, Sex.FEMALE, Citizenship.KOREA) // OK
베스트 프랙티스 3: getter는 자유롭게 추가
- 사용자가 알 필요 없는 정보는 보여주지 않는 게 정석
- 그러나 보여줘도 큰 문제는 없으니 getter는 보통 자유롭게
- 주의: 어떤 개체의 레퍼런스를 반환할 때는 문제 될 수도 있음
public class Family {
private Human[] parents;
private Dog pet;
// ...
public Dog getPet() {
return pet;
}
}
Family adams = new Family();
Family smiths = new Family();
Dog notAdamsDog = smiths.getPet();
adams.setPet(notAdamsDog);
C++는 getter에서 읽기 전용레퍼런스를 반환할 수 있어 이런 문제가 없음
베스트 프랙티스 4: setter는 고민 후 추가
- 이상적인 개체의 상태 수정법
- 그 개체의 사용자가 어떤 동작을 지시
- 그 동작의 결과로 개체 안에 있는 어떤 상태가 바뀜
- 즉, 개체 스스로 상태를 변경
- setter는 데이터를 직접 바꾸므로 가능한 피하는 게 좋음
- 단, 언제나 그럴 수는 없기에 많이들 허용
- 중요한 점: 개체가 불확실한 상태로 되는 경우를 최대한 막자!
- 무조건 setter/getter를 추가하는 경우도 있음
- 혹시나 모를 미래를 위해
- 그냥 아무 생각 없이(...)
이상적인 개체의 상태 수정법 예
public class Classroom {
private int[] scores;
private float mean;
// ...
public boolean setScore(int index, int score){
scores[index] = score;
updateMean();
return true;
}
private void updateMean() {
this.mean = 계산결과;
}
// ...
}
classroom.setScore(1, 100);
classroom.setScore(13, 20);
정리: 캡슐화
- 개체의 데이터(=멤버 변수)와 동작(=메서드)을 하나로 묶음
- 내부의 데이터를 외부로부터 보호
- 사용자가 클래스 속을 알 필요가 없음
- 사용자가 함수 속을 알 필요가 없는 것과 마찬가지
- 이 개념은 추상화로 이어짐
- 함수를 분리할 때 적용했던 원칙을 클래스에도 적용할 것!
- 중복된 코드가 있다면 private 메서드로
정리: 추상화
- 추상 자료형(abstract data type)쪽 관점
- 사용자는 클래스를 자료형으로 사용할 수 있음
- 그 클래스 안에 들어있는 멤버 변수가 정확히 뭔지 몰라도 됨
- 그냥 클래스로부터 개체 생성 가능
- 절차적 데이터 추상화(procdeural data abstraction) 쪽 관점
- 데이터를 직접 조작하는 대신 메서드를 호출
- OOP라는 용어를 처음 주창했다는 소수설의 관점과 유사
- 동작적 개체(behavioral objects) 진영이라고 하기도 함(정식 명칭 x)
추상화의 단점
- 동작 없이 데이터만 있는 클래스는 쓸데 없는 코드만 늘어남
- 예: 웹 프로그래밍에서 많이 볼 수 있는 DTO(data transfer object)
- 그래서 이런 경우에는 그냥 public 데이터를 쓰기도 함
- 어떻게 추상화를 해야하는지 뚜렷한 객관적 기준이 없음
- 나중에 다형성, 상속, 인터페이스에서 나오는 추상화에서 특히 문제
'프로그래머 > Java Managed Programming' 카테고리의 다른 글
[개체지향 프로그래밍] static | 싱글턴 | 내포 클래스 (0) | 2021.03.01 |
---|---|
[개체지향 프로그래밍] 개체 모델링 | 클래스 다이어그램 | 유연성 | OOP (0) | 2021.02.13 |
[개체지향 프로그래밍] 개체 생성 | 가비지 콜렉터(garbage collector) | 생성자(constructor) (0) | 2021.02.07 |
[개체지향 프로그래밍] 클래스(class) | 접근 제어자(public, private) | 인스턴스(instance) (0) | 2021.02.07 |
[개체지향 프로그래밍] 개체지향 프로그래밍이란? | 개체지향 프로그래밍의 필요성 | 개체지향 프로그래밍 특성 (0) | 2021.01.17 |