[21.07.15] singleton 패턴

yed·2021년 7월 15일
0

기능 설계는 기존의 시스템을 보면서 참고 혹은 업그레이드 시켜보면서 시작해봅시다.


final 수식어

final은 최종이라는 뜻으로 final수식어를 쓴 대상의 초기화된 값은 바꿀 수 없습니다. 수정하면 안되는 형태를 누군가 수정해버리면 오류가 생길 수 있으니 그것을 막기 위해 애초에 선언하면서 값을 고정시킬 때 사용해요!

  • final 멤버변수/지역변수 : 상수
  • final 메소드 : override를 할 수 없는 메소드
  • final 클래스 : 상속을 허용하지 않는 클래스

만약 1번을 선택해야 입력하는 기능이 나오는 메뉴를 구성하다면 지금까진 switch문을 사용해 case 1: 입력기능으로 만들었는데요. 메뉴를 선택하는 1이 아니라 2, 3 등 다른 숫자로 수정하게 된다면 제대로 작동하지 않겠죠? 1번을 선택해야만 입력기능을 하는 경우 1이라는 값은 변하면 안됩니다. 이럴 때 final 수식어를 사용하는데요

public static final int MENU_INSERT = 1;

전역변수 MENU_INSERT에 final수식어를 붙였습니다.

switch(select) { 
	case MENU_INSERT: 
	System.out.println("입력");
	break;
default :
	break;
}

case 1이 아니라 MENU_INSERT을 사용해 값을 고정해주면 MENU_INSERT의 값을 바꿔보려해도 오류가 나는것을 알 수 있어요. 또한 정수 1만 적어두는것보다 변수를 사용하면 코드만 봐도 case가 무슨 기능을 하는지 이름으로 알 수 있습니다.

JVM(java Virtual Machine)

java는 다양한 운영체제를 지원하는데요 그 이유는 JVM에게 있습니다. JVM은 컴파일러와 운영체제의 중간 역할로 컴퓨터한테 코드를 전달해줍니다.

java의 명령어 실행과정
소스코드 작성 -> 컴파일러 -> ByteCode 생성 -> JVM -> 운영체제 및 하드웨어-> 실행

JVM이 컴파일러가 변환한 bytecode를 해석해 기계어로 변환하는데 이 bytecode가 운영체제와 상관없이 동일하다는 점! 그래서 각 운영체제에 JVM이 존재만 한다면 bytecode를 해석해 사용할 수 있습니다.

java Runtime이 관리하는 메모리

  1. Stack : 지역변수, 매개변수를 저장하는 메모리 영역
  2. heap : 인스턴스가 저장되는 메모리 영역
  3. method : static으로 선언된 변수와 메소드가 저장되는 메모리 영역
  • 메모리 저장순서 : method -> heap -> stack
    static 수식어를 사용한다면 가장 먼저 메모리에 저장됩니다.

static 수식어

static을 사용한 경우

멤버변수나 메소드를 선언할 때 static을 사용하면 인스턴스를 생성하기 전에 프로그램이 처음 시작하면서 method영역에 먼저 자동 생성됩니다. 또한, 인스턴스를 생성하지 않고도 사용할 수 있습니다.

  1. 클래스 변수 : static 변수라고도 하며 static으로 선언한 멤버변수를 의미합니다. new를 사용해 인스턴스를 생성하지 않아도 사용할 수 있어요! static 변수는 생성되는 모든 인스턴스들이 공유하게 됩니다.

  2. 클래스 메소드 : static 메소드라고도 하며 static으로 선언한 메소드를 의미해요. 인스턴스를 생성하지 않아도 사용할 수 있습니다. 클래스이름.메소드이름()

  3. 인스턴스 변수 : static이 붙지않은 멤버변수예요. 얘는 인스턴스를 생성한 후에만 사용할 수 있어서 참조변수가 꼭 필요합니다. 참조변수.변수이름

  4. 인스턴스 메소드 : static을 사용하지 않은 메소드로 new를 통해 인스턴스를 생성한 후에만 사용할 수 있습니다. 참조변수.메소드이름()

예시를 봅시다.

int num1; //인스턴스 멤버변수. 
static int num2; //클래스 멤버변수. 

public static void display() {
	System.out.println("num1="+num1); //오류!
	System.out.println("num2="+num2);
}

static 메소드 display()에서 num1을 사용하려고 했더니 오류가 났어요. 왜냐하면 인스턴스 멤버변수보다 static 메소드가 먼저 실행되기 때문에 display()가 실행될 땐 num1은 할당되지 않은 변수거든요. 그래서 static 메소드에서는 static이 아닌 멤버 변수는 쓸 수 없습니다.

그렇다면 귀찮게 인스턴스 생성없이 전부 static을 사용하면 되지 않을까 의문이 생기지 않으신가요?😏 당연히 모든 변수나 메소드를 static을 사용해서 인스턴스 생성없이 사용할 수는 있어요!
그러나 시작하면서 동시에 저장되는 수가 많아질수록 부하가 커져 시작 속도가 느려지게 됩니다.😥 부하를 분산시켜야 속도가 빨라져요 초기에 필요한 것만 static으로 선언합시다!

Singleton 패턴

singleton 패턴의 기능을 한마디로 정의하자면 객체의 재활용입니다! 클래스의 인스턴스를 오직 하나만 생성할 수 있게 만드는 패턴인데요. 인스턴스를 한번만 생성해서 같은 인스턴스를 계속 사용할 수 있어요. singleton 인스턴스를 사용한다면 메모리 낭비 막을 수 있고 인스턴스끼리 데이터 공유도 가능해집니다.
하지만 singleton 인스턴스가 여러 곳에서 사용된다면 프로그램의 결합도가 높아지게 됩니다. 결합도가 높으면 프로그램의 유지보수가 어려워지니 필요한 부분에서만 사용해야겠죠?🥴

singleton 패턴 사용

  1. 고정된 메모리 영역에 인스턴스를 한번만 생성하기 때문에 메모리 낭비 방지
  2. singleton 클래스의 인스턴스는 전역 인스턴스데이터 공유가 쉬움
  3. 쓰레드풀, 캐시, 네트워크 연결, DB 연결 등 공통 객체를 사용해야하는 상황에서 사용

System.out.println(new Test() == new Test()); //false

똑같은 Tst() 생성자로 만들어진 인스턴스지만 둘의 주소값은 다릅니다. 주소값이 다르다는 것은 서로 다른 객체라는 것을 의미합니다.

System.out.println(Captain.getInstance() == Captain.getInstance()); //true

반면 싱글톤 인스턴스는 주소값이 똑같아요. 주소값이 같으면 둘은 같은 객체라는 건데요. 주소값이 같은 싱글톤 인스턴스는 저장된 데이터를 공유하게 됩니다. 같은 객체를 공유하면 데이터의 변경이나 삭제도 공유하게 된다는 점! 그래서 데이터 충돌이 일어나지 않게 조심해야해요

설계기법

  1. 자기자신 클래스 타입private static 변수 선언
  2. private로 생성자 선언
  3. public static 메소드를 정의해 인스턴스를 생성

"생성자를 호출하면서 객체를 생성한다"가 객체 생성의 기본형식인데요. 싱글톤 패턴은 인스턴스가 하나만 있으면 충분하기 때문에 인스턴스를 생성할 수 있는 생성자에 아무나 직접 접근할수 없게 private 선언을 합니다.
대신 우회적으로 생성자에 접근할 수 있도록 특정 메소드 안에서 인스턴스를 생성합니다. 인스턴스가 없어도 함수를 불러올 수 있게 public static 메소드를 이용해야해요. 메소드에서 최초 1회에만 인스턴스를 생성하고 그 다음부턴 기존에 만들었던 인스턴스를 반환하면 됩니다.

private static Captain instance=null;
// 생성한 객체를 저장하기 위한 변수. 
	
private Captain() {} //생성자
	
public static Captain getInstance() {
	if(instance==null) {
		System.out.println("Captain 인스턴스 생성");
		instance=new Captain();
	}
	return instance;
}

Captain c1= new Captain(); //오류!

기존엔 인스턴스를 new 생성자로 생성했으나 생성자가 private라 오류가 나죠?

Captain c1=Captain.getInstance();

싱글톤 인스턴스는 static 메소드로 우회해서 인스턴스를 생성합니다!


tip💡

  • shift+alt+r : 이름변경 시 전체 적용하기
  • 같은 용도의 변수와 매개변수의 이름은 동일하게 설정하는게 좋다.

Q. 인스턴스를 생성안해도 되는 변수는 주소값이 없는가?

A. 네!
int a=1; <일반 변수는 할당된 메모리 주소가 존재하긴 하지만
System.out.println(a); <주소를 값으로 넘기진 않습니다.
위를 call by value라고 해요!
그렇다면
Test t=new Test() <참조형 변수는 인스턴스가 생성되면서 주소값이 생겨서
System.out.println(t); <주소를 값으로 넘기게 됩니다.
이런 경우를 call by reference라고 합니다


코로나 4단계인데도 확진자 수가 안줄어서 단계가 낮아질때까지 줌으로 수업을 듣게 되었어요 내용이 점점 본격적으로 어려워지는 기점이라 더욱 아쉽네요 호달달 집중력 문제는.... 온전히 제 몫이니깐..😂 화이팅해보겠습니다

profile
6개월 국비과정 기록하기

0개의 댓글

관련 채용 정보