시그마 삽질==six 시그마

템플릿 메서드 패턴 & 팩토리 메서드 패턴 본문

프로그래밍/디자인패턴

템플릿 메서드 패턴 & 팩토리 메서드 패턴

Ethan Matthew Hunt 2020. 4. 23. 22:11

 

'자바 객체 지향 디자인 패턴' 책을 구입하시길  추천드립니다.

책 구입을 원하시는분은 요기를 클릭하시면 됩니다.

하단의 내용은 제가 예전에 읽었던 내용을 요약 정리한 것입니다.

https://en.wikipedia.org/wiki/Template_method_pattern

1. 템플릿 메소드 패턴

 

동일한 기능을 상위 클래스에서 정의하면서 확장/변화가 필요한 부분만 서브클래스에서 구현하는 패턴

 

공통기능을 담당하는 추상클래스의 특정메소드에 (공통기능은 덤) 추상 메소드를 호출하는 코드를 넣어(hook,primitive메서드) 변화가 필요한 부분을 서브클래스에서 구현할 있게 해놓은것

 

 

 

하단과 같이 오티스 엘리베이터의 모터 클래스가 있다고 하자

 

public class OtisMotor {
	private Door door ;
	private MotorStatus motorStatus ;
	
	public OtisMotor(Door door) {
		this.door = door ;
		motorStatus = MotorStatus.STOPPED ;
	}	
	private void moveOtisMotor(Direction direction) {
		
	}
	public MotorStatus getMotorStatus() {
		return motorStatus;
	}
	private void setMotorStatus(MotorStatus motorStatus) {
		this.motorStatus = motorStatus;
	}
	public void move(Direction direction) {
		MotorStatus motorStatus = getMotorStatus() ;
		if (  motorStatus == MotorStatus.MOVING ) return ;
		
		DoorStatus doorStatus = door.getDoorStatus() ;
		if ( doorStatus == DoorStatus.OPENED )
			door.close() ;
		
		moveOtisMotor(direction) ;
		
		setMotorStatus(MotorStatus.MOVING) ;
	}
}

public class Door {
	private DoorStatus doorStatus ;
	public Door() {
		doorStatus = DoorStatus.CLOSED ;
	}
	public DoorStatus getDoorStatus() {
		return doorStatus ;
	}
	public void close() {
		doorStatus = DoorStatus.CLOSED ;
	}
	public void open() {
		doorStatus = DoorStatus.OPENED ;
	}
}

public enum DoorStatus { CLOSED, OPENED }
public enum Direction { UP, DOWN }
public enum MotorStatus { MOVING, STOPPED}

public class Client {
	public static void main(String[] args) {
		Door door = new Door() ;
		OtisMotor otisMotor = new OtisMotor(door) ;
		otisMotor.move(Direction.UP) ;
	}
}

 

만약 요기서 삼성 모터가 추가가 된다면 하단과 같이 삼성모터 클래스를 만든다.

 

 

public class SamsungMotor {
	private Door door ;
	private MotorStatus motorStatus ;
	
	public SamsungMotor(Door door) {
		this.door = door ;
		motorStatus = MotorStatus.STOPPED ;
	}	
	private void moveSamsungMotor(Direction direction) {
	
	}
	public MotorStatus getMotorStatus() {
		return motorStatus;
	}
	private void setMotorStatus(MotorStatus motorStatus) {
		this.motorStatus = motorStatus;
	}
	public void move(Direction direction) {
		MotorStatus motorStatus = getMotorStatus() ;
		if (  motorStatus == MotorStatus.MOVING ) return ;
		
		DoorStatus doorStatus = door.getDoorStatus() ;
		if ( doorStatus == DoorStatus.OPENED )
			door.close() ;
		
		moveSamsungMotor(direction) ;
		
		setMotorStatus(MotorStatus.MOVING) ;
	}
}


public class Client {
	public static void main(String[] args) {	
		Door door = new Door() ;
		SamsungMotor samsungMotor = new SamsungMotor(door) ;
		samsungMotor.move(Direction.UP) ;
	}
}

 

그런데  오티스 모터와 삼성모터의 코드 중복이 심해진다

 

그래서 모터 추상클래스를 만들고 공통된 부분을 넣는다 

 

public abstract class Motor {
	protected Door door ;
	private MotorStatus motorStatus ;
	
	public Motor(Door door) {
		this.door = door ;
		motorStatus = MotorStatus.STOPPED ;
	}	
	public MotorStatus getMotorStatus() {
		return motorStatus;
	}
	protected void setMotorStatus(MotorStatus motorStatus) {
		this.motorStatus = motorStatus;
	}
}


public class OtisMotor extends Motor {
	public OtisMotor(Door door) {
		super(door) ;
	}	
	private void moveOtisMotor(Direction direction) {
		
	}
	public void move(Direction direction) {
		MotorStatus motorStatus = getMotorStatus() ;
		if (  motorStatus == MotorStatus.MOVING ) return ;
		
		DoorStatus doorStatus = door.getDoorStatus() ;
		if ( doorStatus == DoorStatus.OPENED )
			door.close() ;
		
		moveOtisMotor(direction) ;
		
		setMotorStatus(MotorStatus.MOVING) ;
	}
}



public class SamsungMotor extends Motor {
	public SamsungMotor(Door door) {
		super(door) ;
	}	
	private void moveSamsungMotor(Direction direction) {
		
	}
	public void move(Direction direction) {
		MotorStatus motorStatus = getMotorStatus() ;
		if (  motorStatus == MotorStatus.MOVING ) return ;
		
		DoorStatus doorStatus = door.getDoorStatus() ;
		if ( doorStatus == DoorStatus.OPENED )
			door.close() ;
		
		moveSamsungMotor(direction) ;
		
		setMotorStatus(MotorStatus.MOVING) ;
	}
}

 

 

그럼에도 불구하고 move 메소드 내부는 여전히 겹치는 부분이 많다.

 

이럴때 필요한 것이 템플릿 메소드다.

 

move 메소드 속에 추상메소드인 hook 메소드를 삽입한다.

 

public abstract class Motor {
	private Door door ;
	private MotorStatus motorStatus ;
	
	public Motor(Door door) {
		this.door = door ;
		motorStatus = MotorStatus.STOPPED ;
	}	
	public MotorStatus getMotorStatus() {
		return motorStatus;
	}
	private void setMotorStatus(MotorStatus motorStatus) {
		this.motorStatus = motorStatus;
	}
	public void move(Direction direction) {
		MotorStatus motorStatus = getMotorStatus() ;
		if (  motorStatus == MotorStatus.MOVING ) return ;
		
		DoorStatus doorStatus = door.getDoorStatus() ;
		if ( doorStatus == DoorStatus.OPENED )
			door.close() ;
		
		moveMotor(direction) ; //hook, primitive 메소드라한다
		
		setMotorStatus(MotorStatus.MOVING) ;
	}
	protected abstract void moveMotor(Direction direction) ;
}




public class OtisMotor extends Motor {
	public OtisMotor(Door door) {
		super(door) ;
	}	
	protected void moveMotor(Direction direction) {
		// OtisMotor Motor내부 구현
	}
}


public class SamsungMotor extends Motor {
	public SamsungMotor(Door door) {
		super(door) ;
	}	
	protected void moveMotor(Direction direction) {
		// LG Motor내부 구현
	}
}

 

 

 

실무에서 사용하는 예시...

@Service
public class AaaServiceImpl extends GenericServiceImpl<A, Long> implements AaaService {

	@Resource
	private AaaRepository aaaRepository;
	
	@Override
	public GenericRepository<A, Long> initializingRepository() {
		return aaaRepository;
	}
    .....

 

public abstract class GenericServiceImpl<T, PK extends Serializable> implements GenericService<T, PK> {
    private GenericRepository<T, PK> domainRepository;

    public GenericServiceImpl() {
    }

    public GenericRepository<T, PK> getDomainRepository() {
        if (this.domainRepository == null) {
            this.domainRepository = this.initializingRepository();
        }

        return this.domainRepository;
    }

    public abstract GenericRepository<T, PK> initializingRepository();
    
    
    @Transactional
    public T add(T entity) {
        return this.getDomainRepository().insert(entity);
    }

    @Transactional
    public T edit(T entity) {
        return this.getDomainRepository().update(entity);
    }

    @Transactional
    public void remove(PK id) {
        this.getDomainRepository().delete(id);
    }

    @Transactional(readOnly = true)
    public T findById(PK id) {
        return this.getDomainRepository().selectById(id);
    }

 

 

2. 팩토리 메소드 패턴

 

객체를 생성하는 인터페이스는 미리 정의하고 서브클래스에 인스턴스 결정  책임을 위임

인스턴스를 생성하는 공장을 템플릿 메서드 패턴으로 만든것이 팩토리 메서드 패턴임

 

 

https://en.wikipedia.org/wiki/Factory_method_pattern

 

Comments