[그린컴퓨터] Server/JAVA(객체 지향 프로그래밍)

static 응용 - 싱글톤 패턴 {디자인 패턴이란, 싱글톤 패턴이란, 싱글톤 패턴으로 회사 클래스 구현하기, 연습하기}

Ben의 프로그램 2023. 5. 24. 13:33
728x90

디자인 패턴이란?

  • 객체 지향 프로그램을 어떻게 구현해야 좀 더 유연하고 재활용성이 높은 프로그램을 만들 수 있는지를 정리한 내용이 디자인 패턴 design pattern 입니다.
  • 간단히 말해서 프로그램 특성에 따른 설계 유형을 이론화한 내용이며, 특정 알고리즘이나 프로그래밍 언어를 위한 라이브러리가 아니라 객체 지향으로 설계하는 방법을 설명한 것입니다.
  • 그렇기 때문에 디자인 패턴은 자바, C++, C# 과 같은 다른 객체 지향 언어에도 적용하여 구현할 수 있습니다.
  • 디자인 패턴 이론만으로도 책 한 권을 만들 수 있을 정도로 내용이 방대합니다. 

싱글톤 패턴이란?

  • 프로그램을 구현하다 보면 여러 개의 인스턴스가 필요한 경우도 있고 단 하나의 인스턴스만 필요한 경우도 있습니다. 
  • 객체 지향 프로그램에서 인스턴스를 단 하나만 생성하는 디자인 패턴을 싱글톤 패턴 singleton pattern 이라고 합니다.
  • 우리가 여기서 살펴볼 싱글톤 패턴은 static 을 응용하여 프로그램 전반에서 사용하는 인스턴스를 하나만 구현하는 방식입니다.
  • 실무나 여러 프레임워크에서 많이 사용하는 패턴이므로, 잘 익혀두어야 합니다.

싱글톤 패턴으로 회사 클래스 구현하기

  • 어떤 회사를 객체 지향 프로그램으로 구현한다고 생각해봅시다. 
  • 직원은 여러명일테니 인스턴스를 여러 개를 생성합니다.
  • 회사는 하나일테니 회사 인스턴스(객체)는 하나만 생성해야합니다.
  • 단계적으로 Company 클래스를 만들어 봅시다. 

단계 1 : 생성자를 private으로 만들기

  • 생성자가 하나도 없는 클래스는 컴파일러가 자동으로 디폴트 생성자 코드를 넣어 줍니다. 
  • 그런데, 컴파일러가 만들어 주는 디폴트 생성자는 항상 public입니다. 
  • 생성자가 public이면 외부 클래스에서 인스턴스를 여러 개 생성할 수 있습니다. 
  • 따라서 싱글톤 패턴에서는 생성자를 반드시 명시적으로 만들고 그 접근 제어자를 private으로 지정해야 합니다. 
  • 그러면 컴파일러가 디폴트 생성자를 만들지 않고, 접근 제어자가 private 이므로 외부 클래스에서 마음대로 Company 인스턴스를 생성할 수 없게 됩니다. 
  • 즉 Company 클래스 내부에서만 이 클래스의 생성을 제어할 수 있습니다. 
package singleton;

public class Company {
	
	private Company() {
		
	}
}

단계 2 : 클래스 내부에 static 으로 유일한 인스턴스 생성하기

  • 단계 1에서 외부 클래스에서 인스턴스를 생성할 수 없도록 만들었습니다. 
  • 하지만 우리가 프로그램에서 사용할 하나의 인스턴스(회사 객체)가 필요합니다. 
  • 따라서 Company 클래스 내부에서 하나의 인스턴스를 생성해야 합니다. 
  • 회사 인스턴스(객체)는 프로그램 전체에서 사용할 유일한 인스턴스가 됩니다. 따라서 해당 인스턴스를 static 키워드를 포함한 상태로 만듭니다.
  • 또한 외부에서 이 인스턴스에 접근하지 못하도록 private 키워드를 포함한 상태로 만들어야 인스턴스 오류를 방지할 수 있습니다.
package singleton;

public class Company {
	private static Company instance = new Company();
	private Company() {}
}

단계 3 : 외부에서 참조할 수 있는 public 메서드 만들기

  • 이제 private 으로 선언한 유일한 인스턴스를 외부에서도 사용할 수 있도록 설정해야 합니다. 
  • 이를 가능하게 만드는 방법은 public 메서드(getter 메서드)를 생성하는 것입니다. 
  • 해당 메서드에서 유일하게 생성한 instance 를 반환해 줍니다. 
  • 이때 인스턴스를 반환하는 메서드는 반드시 static 으로 선언해야 합니다.
  • 왜냐하면 getInstance( ) 메서드는 인스턴스 생성과 상관없이 호출할 수 있어야 하기 때문입니다. 
package singleton;

public class Company {
	private static Company instance = new Company();
	private Company() {}
	public static Company getInstance() {
		if(instance == null) {
			instance = new Company();
		}
		return instance;
	}
}

단계 4 : 실제로 사용하는 코드 만들기

  • 외부 클래스에서는 Comapny 를 생성할 수 없으므로 static 으로 제공되는 getInstance( ) 메서드를 호출합니다.
  • Company.getInstance( ) 와 같이 호출하면 반환 값으로 유일한 인스턴스를 받아 옵니다. 
  • 다음 예제에서 유일한 인스턴스를 대입한 두 변수의 주소 값이 같은지 확인해 보면 true 가 출력되는 것을 볼 수 있습니다.
package singleton;

public class CompanyTest {
	public static void main(String[] args) {
		Company myCompany1 = Company.getInstance();
		Company myCompany2 = Company.getInstance();
		
		System.out.println(myCompany1 == myCompany2);
	}

}

출력값

  • myCompany1 과 myCompany2 를 비교해 보면 같은 참조 값을 가지는 동일한 인스턴스임을 알 수 있습니다.
  • 또한 Company 클래스는 내부에 생성된 유일한 인스턴스 외에는 더 이상 인스턴스를 생성할 수 없습니다. 
  • 이와 같이 static 을 사용하여 유일한 객체를 생성하는 싱글톤 패턴을 구현할 수 있습니다.

연습하기

  • 자동차 공장 클래스를 만들어 봅시다.
  • 자동차 공장은 유일한 객체입니다.
  • 여기서 생산되는 자동차는 100001 번 부터 시작해서 1씩 증가하면서 자동차 번호가 부여됩니다. 
  • ** Car, Carfactory, CarfactoryTest 클래스를 만들어서 프로그램을 구성합니다. 
  • 다음 CarFactoryTest.java 테스트 코드가 수행될 수 있도록 구현합니다.
package singleton;

public class CarFactoryTest {
	public static void main(String[] args) {
		CarFactory factory = CarFactory.getInstance();
		Car mySonata = factory.createCar("mySonata");
		Car yourSonata = factory.createCar("youSonata");
		System.out.println(mySonata.getCarNum());
		System.out.println(yourSonata.getCarNum());
		System.out.println(mySonata.toString());
		System.out.println(yourSonata.toString());
	}

}
  • ===== 정답 =====
package singleton;

public class Car {
	String name;
	static int serialNum = 10000;
	int carNum;
	int carCount=0;
	
	public Car(String name) {
		serialNum++;
		this.carNum = serialNum;
		this.name = name;
		this.carCount++;
	}
	
	@Override
	public String toString() {
		return "Car [name=" + name + ", carNum=" + carNum + ", carCount=" + carCount + "대 생성]";
	}

	public int getCarNum() {
		return carNum;
	}

	public void setCarNum(int carNum) {
		this.carNum = carNum;
	}
}

 

package singleton;

public class CarFactory {
	
	private CarFactory() {} // 싱글톤 패턴 생성자
	
	private static CarFactory instance = new CarFactory(); // 싱글톤 패턴 유일 인스턴스
	
	public static CarFactory getInstance() { // 싱긅톤 패턴 유일 인스턴스 getter
		return instance;
	}
	
	public Car createCar(String name) {
		Car car = new Car(name);
		return car;
	}
}

출력값