디자인패턴 (1)

hyeon·2022년 7월 18일
0

Computer Science

목록 보기
3/6

디자인패턴

디자인 패턴이란 프로그램을 설계할 때 발생 했던 문제들을 객체간의 상호 관계 등을 이용하여 해결할 수 있도록 하나의 규약 형태로 만들어 놓은 것을 말한다.

(1) 싱글톤 패턴

개념

하나의 클래스에 오직 하나의 인스턴스만 가지는 패턴이다. 하나의 인스턴스를 만들어 놓고 그 인스턴스를 재활용하는것.
아래 코드를 봤을 때 싱글톤 a, b 여러번 객체를 생성하는게 아닌가 생각했는데 생성자가 여러번 호출되도 제로 생성되는 객체는 하나이며 최초로 생성된 이후에 호출된 생성자는 이미 생성한 객체를 반환시키도록 만드는 것라고한다.
(java에서는 생성자를 private으로 선언해 다른 곳에서 생성하지 못하도록 만들고, getInstance() 메소드를 통해 받아서 사용하도록 구현한다)
데이터베이스 연결 모듈(커넥션풀), 스레드 풀, 캐시, 로그 기록 객체 등에 많이 사용됨

객체와 인스턴스의 차이점

객체 : 소프트웨어 세계에 구현해야할 대상 (소프트웨어 관점에서 concept 일뿐)
클래스 : 객체를 구현하기 위한 설계도
인스턴스 : 소프트웨어 세계에 구현된 실체 (설계도를 바탕으로 객체를 소프트웨어에 실체화 하면 인스턴스, 인스턴스는 객체에 포함된다)

java에서의 싱글톤 패턴

class Singleton{
    private static class singleInstanceHolder{	//외부에서 생성자를 호출할 수 없게  private로 선언한다. 
        private static final Singleton Instance=new 	Singleton(); 	//자신을 멤버로 선언하여 메모리에 올려 놓기 (static)
    }		//그래도 private이므로 
    public static synchronized Singleton getInstance() {
    //외부에서 멤버로 선언된 car를 가져올 수 있는 method를 만든다
        return singleInstanceHolder.Instance;
    }
}
public class HelloWorld {

    public static void main(String[] args) {
        Singleton a=Singleton.getInstance();
        Singleton b=Singleton.getInstance();
        System.out.println(a.hashCode());   //해시알고리즘에 의해 생성된 정수 값
        //equals 비교에 사용되는 정보가 변경되지 않는다면 애플리케이션이 실행되는 동안 그 객체의 hashcode메소드는 항상 같은 값을 반환한다.
        System.out.println(b.hashCode());
        if(a==b) {
            System.out.println(true);
        }
   
    }
    


}

싱글톤 패턴의 장 단점

장점

객체를 생성할 때마다 메모리 영역을 할당받아야 한다. 하지만 한번의 new를 통해 객체를 생성한다면 메모리 낭비를 방지할 수 있다.
또한 싱글톤으로 구현한 인스턴스는 '전역'이므로, 다른 클래스의 인스턴스들이 데이터를 공유하는 것이 가능한 장점이 있다.

단점

싱글톤 인스턴스가 혼자 너무 많은 일을 하거나, 많은 데이터를 공유시키면 다른 클래스들 간의 결합도가 높아져서 객체지향 설계원칙 중 개방-폐쇄 원칙가 위배된다.

결합도가 높아지게 되면, 유지보수가 힘들고 테스트도 원활하게 진행할 수 없는 문제점이 발생한다. 그래서 TDD를 할 때 단위테스트를 주로 하는데 단위테스트는 서로 독립적이어야 하며 테스트를 어떤 순서로든 수행할 수 있어야한다. 하지만 싱글톤 패턴은 하나의 인스턴스를 기반으로 구현하는 패턴이므로 독립적인 인스턴스를 만ㄷ르기가 어렵다.

또한, 멀티 스레드 환경에서 동기화 처리를 하지 않았을 때, 인스턴스가 2개가 생성되는 문제도 발생할 수 있다.

멀티 스레드에서 안전한 싱글톤 만들기

<동기화>
임계영역(멀티 스레드에 의해 공유자원이 서로 참조될 수 있는 코드의 범위)에서 스레드들이 순서를 갖춰 자원을 사용하게 하는 것을 동기화라고한다. 동기화 메소드를 구현하기 위해서는 synchronized 키워드를 사용한다. 동기화를 처리하기위해 모든객체에 락을 포함 시켰다. 락은 모든 객체가 힙영역에 생성될 때 자동으로 만들어진다.

  1. Lazy Initialization (게으른 초기화)
    클래스 로더가 초기화하는 시점에서 정적 바인딩을 통해 해당 인스턴스를 메모리에 등록하여 사용하는 것 (static의 특징을 활용) 클래스 로더에 의해 클래스가 처음 로딩되는 시점에 메모리에 로드하기 때문에 Thread-safe를 보장하게 됨
    하지만 Synchronized는 큰 성능저하를 발생시킴
public class ThreadSafe_Lazy_Initialization{
 
    private static ThreadSafe_Lazy_Initialization instance;
 
    private ThreadSafe_Lazy_Initialization(){}
     
    public static synchronized ThreadSafe_Lazy_Initialization getInstance(){
        if(instance == null){
            instance = new ThreadSafe_Lazy_Initialization();
        }
        return instance;
    }
 
}
  1. 1번 +Double- checked locking
    먼저 조건문으로 인스턴스의 존재 여부를 확인한 다음 두번째 조건문에서 synchronized를 통해 동기화 시켜 인스턴스를 생성하는 방법
public class ThreadSafe_Lazy_Initialization{
    private volatile static ThreadSafe_Lazy_Initialization instance;

    private ThreadSafe_Lazy_Initialization(){}

    public static ThreadSafe_Lazy_Initialization getInstance(){
    	if(instance == null) {
        	synchronized (ThreadSafe_Lazy_Initialization.class){
                if(instance == null){
                    instance = new ThreadSafe_Lazy_Initialization();
                }
            }
        }
        return instance;
    }
}
  1. holder에 의한 초기화
    클래스 안에 클래스(holder)를 두어 JVM의 클래스 로더 매커니즘과 클래스가 로드 되는 시점을 이용한 방법 (가장 많이 사용하는 방법)
    클래스 안에 선언한 클래스인 holder에서 선언된 인스턴스는 static이기때문에 클래스 로딩 시점에서 한번만 호출된다. 또한 final을 사용해서 다시 값이 할당 되지 않도록 만드는 방식
    (클래스가 로딩되는 시점에 바로 인스턴스를 생성하는 것이 아니라 getInstance로 메서드가 호출될 때 JVM 메모리에 로드되고 인스턴스를 생성하는 방식이다)
public class Something {
    private Something() {
    }
 
    private static class LazyHolder {
        public static final Something INSTANCE = new Something();
    }
 
    public static Something getInstance() {
        return LazyHolder.INSTANCE;
    }
}

(2)팩토리 패턴

개념

팩토리 패턴은 객체를 사용하는 코드에서 객체 생성 부분을 떼어내 추상화한 패턴이다.
= 객체를 만드는 부분을 Sub Class에 맡기는 패턴
상속관계의 두 클래스에서 상위클래스가 중요한 뼈대를 결정하고 하위 클래스에서 객체 생성에 관한 구체적인 내용을 결정하는 패턴이다.

객체지향의 디자인패턴 기본원칙중 개방 폐쇠 원칙, 확장에 있어서는 열려있어야 하며, 수정에 있어서는 닫혀 있어야 한다에서 수정이 일어날 가능성이 큰 부분과 그렇지 않은 부분을 분리하는 것이 좋다는 것을 알 수 있다. 그래서 팩토리 패턴이 나타내게 된것이다.

java에서의 팩토리 패턴

public abstract class RobotFactory {
	abstract Robot createRobot(String name);
}
public class SuperRobotFactory extends RobotFactory {
	@Override
	Robot createRobot(String name) {
		switch(name) {
		case "super" :
			return new SuperRobot();
		case "power" :
			return new PowerRobot();
		}
		return null;
	}
}

아래의 클래스는 RobotFactory 클래스를 상속하고 있기 때문에 반드시 createRobot을 선언해야함. name으로 건너오는 값에 따라서 생성되는 Robot이 다르게 설계된다.

숫자를 전달하거나 문자열을 전달함에 따라 다른 타입의 객체를 생성한다.

팩토리 패턴의 장단점

장점

  • 상위클래스와 하위클래스가 분리되기 때문에 느슨한 결합을 가진다.
  • 상위 클래스에는 인스턴스 생성 방식에 대해 전혀 알 필요가 없기 때문에 많은 유연성을 갖게됨.
  • 객체 생성하는 부분이 따로 떼어져 있기때문에 리팩터링하기 좋아 유지보수성이 증가됨.
  • 단일 책임 원칙과 개방 폐쇄 원칙을 따른다.

단점

패턴을 구현할 많은 서브 클래스를 도입함으로써 코드가 복잡해 질 수 있음

팩토리 메소드 패턴과 추상 팩토리 패턴

  1. 팩토리 메소드 패턴 : 객체를 생성하는 인터페이스를 정의 함으로써, 어떤 인스턴스를 생성할 지는 하위 클래스에서 결정하는 패턴이다.
  2. 추상 팩토리 패턴 : 팩토리들을 그룹으로 묶어 관리할 수 있는 패턴이다. 다른 점은 팩토리를 만드는 상위 팩토리(super-factory) 클래스가 존재한다.

(3) 전략 패턴

개념


->

객체의 행위를 바꾸고 싶은 경우 직접 수정하지 않고 전략이라고 부르는 캡슐화한 알고리즘을 컨텍스트 안에서 바꿔주면서 상호 교체가 가능하게 만드는 패턴이다 .

레퍼런스

링크텍스트
링크텍스트
링크텍스트
링크텍스트

profile
남기고 싶은 개발자입니다 :>

0개의 댓글