객체지향 프로그래밍과 설계(5)
업데이트:
POCU의 개체지향 프로그래밍 및 설계 강의를 듣고 정리한 내용입니다.
static
System.out.println("hello");
out
객체를 생성한 적이 없었는데, 메서드 호출이 가능?
static을 사용하면 전역 변수나 함수처럼 사용 가능하다.
모든 것이 객체 속에 있는 불편함
- 단순한 계산도 객체를 생성해야 하나?
- 절댓값 구하기, 공백 문자 개수 구하기
- 객체 단위가 아닌 클래스 단위에서 연산하고 싶을 때?
- ex) 클래스에서 객체를 몇 개 만들었나?
public static int abs(int n) {
return n < 0 ? -n : n;
}
- 멤버 함수 시그니처에
static
만 붙여주면 됨 - 함수의 소유주는 인스턴스가 아닌 클래스
<개체명>.<함수명>();
과 같이 호출 가능하나 권장 X
정적 클래스와 생성자
클래스로부터 생성한 객체에서도 정적 메서드가 호출 가능하다.
- 객체 생성을 하지 못하게 하면 안되나?
private
생성자를 이용해 객체 생성 금지public class Math { private Math() { } public static int abs(int n) { return n < 0 ? -n : n; } ... }
new
키워드로 생성 시 컴파일 에러- static 메서드는
public
이므로 호출 가능
정적 멤버 변수
클래스에서 객체를 몇 개 만들었나?
개체보다 상위 개념인 클래스의 static
멤버 변수 사용
public class ColaCan {
private int remainingMl;
private static int numCreated;
public ColaCan(int initialMl) {
this.remainingMl = intialMl;
++numCreated;
++this.numCreated; //OK
++ColaCan.numCreated; // OK
}
}
정적 메서드에서 멤버 변수 접근
public class ColaCan {
private int remainingMl;
private static int numCreated;
...
public static void printStats() {
System.out.println("# Cola Produced : " + numCreated);
}
}
remainingMl
멤버변수에는 접근 가능할까?- 클래스 레벨에 속한 메서드가 객체 레벨에 속한 멤버(함수/변수)에 접근 불가
- static 메서드는 어떤 객체의 상태를 특정할 수 없다.
static 정리
- static 멤버 변수 및 멤버 함수는 클래스에 속함 (단 하나만 존재)
- static이 아닌 것은 객체에 속함 ( 객체 수만큼 존재 )
- 비정적 -> 정적 : 접근 가능
- 정적 -> 비정적 : 접근 불가능
static이 C의 전역 변수보다 좋은 점
- 클래스 단위로 접근 범위를 제어할 수 있음
- 클래스 내부에 위치하므로 이름 충돌이 적다
디자인 패턴
- 소프트웨어 설계에서 흔히 겼는 문제에 대한 해결책
- 범용적, 반복적
- 완성된 설계가 아님
장점
- 이미 테스트를 마친 검증된 개발 방법을 사용해 개발 속도를 향상
- 공통 용어 정립을 통한 개발자들 간의 빠른 의사소통 촉진
단점
- 고치려는 대상이 잘못됨
- 곧바로 적용할 수 없는 참고 가이드를 ‘패턴’이라 할 수 없음
- 잘못 적용하는 경우가 빈번함
- 비효율적인 해법이 되는 경우가 많음
- 다른 추상화 기법과 크게 다르지 않음
공부법
패턴을 적용해볼 생각은 최소 2년 경력 이후에! 내 코드가 정확히 어떻게 도는지 이해될 때까지 기본에 집중해라. 프로그래밍 처음 하는 사람에게 쉽게 설명할 수 있을 정도로.
싱글턴
- 클래스에서 만들 수 있는 인스턴스 수를 하나로 제한하는 패턴
- 프로그램 실행 중 최대 하나만 있어야 함
- 프로그램 설정, 파일 시스템 등
- 이 객체에 전역적으로 접근이 가능해야 함
public class Singleton {
private static Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
- private 생성자
- static 메서드를 통해서만 객체를 얻어올 수 있음
- 객체가 없는 경우
- 객체 생성 후 static 변수에 저장
- static 변수에 저장된 객체를 반환
- 생성된 객체가 있는 경우
- static 변수에 저장되 있는 객체를 반환
예제
Math
public class Math {
private static Math instance;
private Math(){
}
public static Math getInstance(){
if (instance == null) {
instance = new Math();
}
return instance;
}
public int abs(int n){
return n < 0 ? -n : n;
}
}
int absValue = Math.abs(-2); // 컴파일 에러
Math math = Math.getInstance();
int minValue = math.min(-2, 1);
int maxValue = Math.getInstance().max(3, 100);
- 싱글턴을 만드는 데 필요한 것만 static
- 나머지는 다 일반 메서드
- 멤버 변수들도 static 아님
싱글턴 vs static
static으로 못 하는 일
- 다형성을 사용할 수 없다
- 시그니처를 그대로 둔 채 멀티턴(multiton) 패턴으로 바꿀 수 없다.
- 멀티턴 : 객체를 하나가 아닌 여러개
- 객체의 생성 시점을 제어할 수 없다
- static은 프로그램 실행 시 초기화됨
- 단, 싱글턴 사용해도 제어에 어려움이 있다.
싱글턴 객체의 생성 시기
- 처음으로
getInstance()
호출될 때 - 하지만 보통 다양한 객체에서
getInstance()
를 호출함- 싱글턴의 생성 순서를 알아내기 힘듬
초기화 순서를 보장하는 방법
- 프로그램 시작 시 여러 싱글턴의
getInstance()
를 순서대로 호출B.getInstance(); C.getInstance(); A.getInstance();
싱글턴 패턴의 응용
싱글턴 생성 시 인자가 필요한 경우
public class GraphicsResourceManager {
...
public static GraphicsResourceManager getInstance(FileLoader loader, GraphicsDevice gfxDevice) {
if (instance == null) {
instance = new GraphicsResourceManager(loader, gfxDevice);
}
return instance;
}
}
- 최초 생성 시에는 매개변수를 넣어주지만, 다른 클래스에서 싱글턴 객체를 받아올 떄 매개변수를 모른다면??
- 객체 생성과 반환을 동시에 구현하기 힘듬
public static void createInstance(FileLoader loader, GraphicsDevice gfxDevice) {
assert (instance == null) : "do not create instance twice";
instance = new GraphicsResourceManager(loader, gfcDevice);
}
public static void deleteInstance() {
assert (instance != null) : "no instance to delete";
instance = null;
}
public static GraphicsResourceManager getInstance() {
assert (instance != null) : "no instance was created before get()";
return instance;
}
내포 클래스 nested class
- 클래스 안에 다른 클래스가 들어가 있는 구조
- java에서는 크게 둘로 나뉨
- 정적 내포 클래스
- 비정적 내포 클래스 = 내부 클래스
용도
- 서로 연관된 클래스들을 그룹지을 수 있음
- 내포 클래스는 바깥 클래스의
private
멤버에 접근 가능
내포 클래스를 사용 안 할 경우
public class Record {
final byte[] rawData;
public Record(byte[] rawData) {
this.rawData = rawData;
}
}
public class RecordReader {
private final Record record;
private int position;
public RecordReader(Record record){
this.record = record;
}
public boolean canRead() {
return this.position < this.record.rawData.length;
}
public byte readByte() {
return this.record.rawData[this.position++];
}
...
}
//main.java
Record record = new Record(fileData);
RecordReader reader0 = new RecordReader(record);
RecordReader reader1 = new RecordReader(record);
비정적 내포 클래스를 사용할 경우
public class Record {
private final byte[] rawData;
public Record(byte[] rawData) {
this.rawData = rawData;
}
public class Reader {
private int position;
public boolean canRead() {
return this.position < rawData.length;
}
public byte readByte() {
return rawData[this.position++];
}
...
}
}
// main.java
Record record = new Record(fileData);
Record.Reader reader0 = record.new Reader();
Record.Reader reader1 = record.new Reader();
정적 내포 클래스를 사용할 경우
public class Record {
private final byte[] rawData;
public Record(byte[] rawData) {
this.rawData = rawData;
}
public static class Reader {
private final Record record;
private int position;
public Reader(Record record){
this.record = record;
}
public boolean canRead(){
return this.position < this.record.rawData.length;
}
}
}
// main.java
Record record = new Record(fileData);
Record.Reader reader0 = new Record.Reader(record);
Record.Reader reader1 = new Record.Reader(record);
- Reader 클래스에 static을 붙임
- static 클래스라는 의미 아님
- 바깥 클래스의 레퍼런스가 없다는 의미
- 비정적 내포 클래스는 암시적으로 컴파일러에서 정적 내포 클래스로 바꾸는 것
댓글남기기