[개체지향] static, 싱글턴, 내포
static
개체의 불편함
-
단순한계산도 개체를 만듬
-
개체단위가아닌 클래스단위에서 뭔가 해주려고할때?
public class Math{
public static int abs(int n){
...
}
public static int min(int n){
...
}
}
//main
int absValue = Math.abs(-2);
int minValue = Math.abs(100,-200);
멤버 함수 시그내처에 static 붙이기. 멤버 함수의 소유주는 인스턴스가 아닌 클래스
UML에선 밑줄로 스태틱을 의미
Math 개체 생성을 못하는 것이 좀 더 올바름 따라서 Math 생성자를 private으로 지정
UML public + private -
java와 c++은 지원안함
static 멤버 변수
public class Colacan{
private static int numCreated; //객체 내의 전역변수
public ColaCan(int initialMl){
++this.numCreated; //(O)
++ColaCan.numCreated; //(O)
++numCreated; //(O)
}
}
정적 메서드에서 비정적 메서드 접근하기 예
public class Colacan{
private static int numCreated; //객체 내의 전역변수
private int remainingMl;
public static void printStats(){
System.out.println("# Cola Produced" + numCreated);
System.out.println("#ml left" + this.remainingMl);//(X)
}
}
클래스에 속한 메서드가 개체에 속한 멤버에 접근 불가.
static 정리
- static 멤버 변수 및 멤버 함수는 클래스에 속함
- static 아닌것은 개체에 속함
- 비정적 -> 정적 : 접근 가능
- 정적 -> 비정적 : 접근 불가능
장점 접근 범위를 제어할 수 있고 클래스 내부에 위치하기 때문에 이름 충돌이 적음.
정적 Logger 클래스
static 비판
static을 쓰는게 OO의 개념과 먼 것은 사실
개체이면서 static과 사실상 똑같은 OO설계법을 만듬
singleton pattern
디자인 패턴
- 인간은 최고의 패턴인식 머신
- 패턴을 이용해 문제를 많이 해결해옴
-
반복을 통해 정형화된 문제 해결 방법
- 소프트웨어 설계에서 흔히 겪는 문제에 대한 해결책
- 범용적, 반복적
- 완성된 설계가 아님 : 가이드 정도로 생각할것
- GoF(Gang of Four)라고 불리는 저자들의 1994년 책에서 등장
장점
- 이미 테스트를 마친 검증된 개발 방법을 사용해 개발 속도를 향상
- 공통 용어 정립을 통한 개발자들 간의 빠른 의사소통을 촉진
단점
- 고치려는 대상이 잘못됨 : c++언어의 미지원 기능에 대한 대비책
- 곧바로 적용할 수 없는 참고 가이드를 패턴이라 부를 수 없음
- 잘못 적용하는 경우가 빈번함, 오히려 프로그램이 복잡해짐
- 비효율적인 해법이 되는 경우가 많음 : 코드 중복이 많아지고 성능이 떨어질 수 있음
- 다른 추상화 기법과 크게 다르지 않음
다룰 종류
- 팩토리 메서드(Factory Method)
- 빌더(builder)
- 싱글턴(singleton)
- 어댑터(래퍼)
- 프록시(proxy)
- 책임 연쇄(chain of responsibility)
- 옵저버(observer)
싱글턴
- 인스턴스 수를 하나로 제한하는 패턴
- 프로그램 실행 중에 최대 하나만 있어야 함.
- 전역적으로 접근이 가능해야함
private 생성자 getInstance() : 개체 반환
public class Singleton{
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
//main
Singlton instace0 = Singleton.getInstace();
Singlton instace1 = Singleton.getInstace();
Singleton vs static
static으로 못하는 일
- 다형성 사용 불가
- 멀티턴패턴(multiton)으로 바꿀 수 없음
- 개체의 생성 시점을 제어할 수 없다.
Singleton에서 초기화 순서를 보장하는 방법
- 프로그램 시작시 여러 싱글턴의 getInstance()를 순서대로 호출
Singleton 패턴의 응용
- 싱글턴 생성지 인자가 필요할 수 있음
- 현재 구현으로는 표현이 어려움
변형예시
//프로그램 실행시 호출
public static void createInstace(FileLoader loader, GraphicsDevice gfxDevice){
assert (instance == null) : "do not create instance twice";
instace = new GraphicsResourceManager(loader, gfxDevice);
}
//종료시,메모리해제
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;
}
anti-pattern : 올바른 방법이 아님 예시 : 매직 넘버, 매직 스트링
내보클래스(중첩 클래스,nested class)
- 비정적 내포 클래스(non-static nested class)
- 정적 내포 클래스(static nested class)
C#, C++등의 언어에서는 정적 내포 클래스만 존재 그래서 보통 inner, nested라는 용어를 혼용해서 사용
용도
- 서로 연관된 클래스들을 그룹 지을 수 있음
- 패키지로 그룹 짓는 것도 가능
- 하지만 클래스 속에 넣는 것이 더 긴밀한 그룹
- 내포 클래스는 바깥 클래스의 private멤버에 접근가능 : 반대는 x
ex Record
neseted 안씀
public class Record{
private 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++];
}
public String readSignature(){
byte ch0 = readByte();
byte ch1 = readByte();
byte ch2 = readByte();
byte ch3 = readByte();
return String.format("%c%c%c%c", ch0, ch1, ch2, ch3);
}
}
//main
Record record = new Record(fileData);
RecordReader reader = new REcordReader(record);
nested 사용
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 this.record.rawData[this.position++];
}
public String readSignature(){
byte ch0 = readByte();
byte ch1 = readByte();
byte ch2 = readByte();
byte ch3 = readByte();
return String.format("%c%c%c%c", ch0, ch1, ch2, ch3);
}
}
}
//main
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;
}
public byte readByte(){
return this.record.rawData[this.position++];
}
public String readSignature(){
byte ch0 = readByte();
byte ch1 = readByte();
byte ch2 = readByte();
byte ch3 = readByte();
return String.format("%c%c%c%c", ch0, ch1, ch2, ch3);
}
}
}
//main
Record record = new Record(fileData);
Record.Reader reader0 = new Record.Reader();
Record.Reader reader1 = new Record.Reader();
Reader 클래스에 static을 붙임
- static 클래스라는 의미는 아님
- 바깥 클래스의 레퍼런스가 없다는 의미
문법이 좀더 깔끔해짐
내포 클래스는 protected와 private도 허용