해당 포스팅에 사용된 예제는 이 블로그 를 참고했습니다
개요
등장 배경
- 객체 지향 디자인 패턴의 기본 원칙(OCP; Open Closed Principle)
- “확장에 있어서는 열려 있어야 하며, 수정에 있어서는 닫혀있어야 한다”
- 코드를 수정하지 않아도 모듈의 기능을 확장하거나 변경할 수 있어야 함
- 객체는 속성과 함수가 변경, 추가될 가능성이 높음 → 객체의 생성을 담당하는 코드는 변경될 가능성이 높음
- 객체의 생성을 담당하는 클래스를 한 곳에서 관리하여 클라이언트 코드와의 결합도를 줄이기 위해 사용
- 결합도(의존도)란 한 클래스의 변경 사항이 다른 클래스에 얼마나 영향을 주는가를 의미
- 클라이언트 코드란 분리시킨 객체 생성 코드를 호출하는 쪽을 말함
정의 및 특징
- 객체를 생성하는 작업을 (팩토리) 클래스에 따로 모아두는 것을 의미
- 상속 관계에서 부모 클래스가 중요한 뼈대를 결정하고, 자식 클래스가 객체 생성에 관한 구체적인 내용을 결정함
- 상위 클래스와 하위 클래스가 분리되기 때문에 느슨한 결합을 가짐
- 크게 2가지 종류를 가짐
- 팩토리 메소드 패턴: 객체를 생성하기 위한 인터페이스를 정의할 때, 어떤 클래스의 인스턴스를 만들지 서브 클래스에서 결정
- 추상 팩토리 패턴: 서로 연관된, 또는 의존하는 객체를 구상 클래스를 지정하지 않고도 인터페이스를 이용하여 생성할 수 있음
(ex: 핸드폰=기종+OS+…) - 추상 팩토리 패턴에 팩토리 메소드 패턴이 포함될 수 있음
장점
- 서로 간의 결합도를 낮추고 확장을 쉽게 함 → 유지보수성 향상
- 상위 클래스에서는 인스턴스 생성 방식에 대해 전혀 알 필요가 없기 때문에 더 많은 유연성을 가짐
- 사용자가 사용할 때는 정의된 인터페이스에 정의된 추상 메소드만 사용하면 됨
- 단일 책임 원칙을 따름. 프로그램의 코드에서 생성자 코드를 분리함으로써 코드를 더욱 간결하게 만들 수 있음
- 개방 폐쇄 원칙을 따름. 기존 client의 코드를 파괴하지 않고 새로운 타입을 추가할 수 있음
활용
- 어떤 클래스가 자신이 생성해야 하는 객체의 클래스를 예측할 수 없는 경우
- 생성할 객체를 기술하는 책임을 자신의 서브 클래스가 지정했으면 할 경우
- java.util 패키지에 있는 Calendar, ResourceBundle, NumberFormat 등의 클래스에 정의된 getInstance() 메소드에서 팩토리 패턴을 사용
- Boolean, Integer, Long 등 Wrapper 클래스에 정의된 valueOf() 메소드에서 팩토리 패턴을 사용
단점
- 패턴을 구현할 많은 서브 클래스를 도입함으로써 코드가 복잡해질 수 있음
종합
- 팩토리 패턴에 사용되는 슈퍼 클래스는 인터페이스나 추상 클래스, 혹은 그냥 평범한 자바 클래스여도 상관 없음
- 팩토리 클래스를 Singleton으로 구현해도 되고, 서브 클래스를 리턴하는 static 메소드로 구현해도 됨
- 팩토리 메소드는 입력된 파라미터에 따라 다른 서브 클래스의 인스턴스를 생성하고 리턴함
팩토리 메소드 패턴
정의 및 특징
- 객체를 생성하기 위한 인터페이스는 미리 정의하되, 어떤 클래스의 인스턴스를 만들지는 서브 클래스에서 결정하도록 함
- 즉, 클래스의 인스턴스를 만드는 일을 서브클래스에게 맡기는 것
- 여러 개의 서브 클래스를 가진 슈퍼 클래스가 있을 때, 인풋에 따라 하나의 자식 클래스의 인스턴스를 리턴해주는 방식
예제 코드
1. 프로토콜 생성
protocol Shape {
func draw()
}
2. 클래스 생성
class Rectangle : Shape {
func draw() {
print("Inside Rectangle::draw() method")
}
}
class Square : Shape {
func draw() {
print("Inside Square::draw() method")
}
}
class Circle : Shape {
func draw() {
print("Inside Circle::draw() method")
}
}
3. 팩토리 생성
class ShapeFactory {
public func getShape(shapeType : String) -> Shape? {
if shapeType == nil {
return nil
}
if shapeType == "CIRCLE" {
return Circle()
}
else if shapeType == "RECTANGLE" {
return Rectangle()
}
else if shapeType == "SQUARE"{
return Square()
}
return nil
}
}
4. 데모
let shapeFactory = ShapeFactory()
let shape1 = shapeFactory.getShape(shapeType: "CIRCLE")
shape1?.draw()
let shape2 = shapeFactory.getShape(shapeType: "RECTANGLE")
shape2?.draw()
let shape3 = shapeFactory.getShape(shapeType: "SQUARE")
shape3?.draw()
5. 출력 결과
Inside Circle::draw() method
Inside Rectangle::draw() method
Inside Square::draw() method
추상 팩토리 패턴
정의 및 특징
- 팩토리 메소드 패턴과 유사하나, 팩토리를 만드는 상위 팩토리(super-factory) 클래스가 존재
- 연관된 서브 클래스를 그룹화할 수 있고 그 그룹을 자유롭게 교체할 수 있는 패턴
- 인터페이스를 이용하여 구체적인 클래스에 의존하지 않고 서로 연관되거나 의존적인 객체들의 조합을 생성함
예제 코드
1. 클래스 생성
class RoundedRectangle : Shape {
func draw() {
print("Inside RoundedRectangle::draw() method")
}
}
class RoundedSquare : Shape {
func draw() {
print("Inside RoundedSquare::draw() method")
}
}
2. 슈퍼 팩토리 생성
protocol AbstractFactory {
func getShape(shapeType : String) -> Shape?
}
3. 하위 팩토리 생성
class ShapeFactory : AbstractFactory {
public func getShape(shapeType : String) -> Shape? {
if shapeType == "RECTANGLE" {
return Rectangle()
} else if shapeType == "SQUARE"{
return Square()
}
return nil
}
}
class RoundedShapeFactory : AbstractFactory {
public func getShape(shapeType : String) -> Shape? {
if shapeType == "RECTANGLE" {
return RoundedRectangle()
} else if shapeType == "SQUARE"{
return RoundedSquare()
}
return nil
}
}
4. 팩토리를 생성하는 Producer 생성
class FactoryProducer {
static func getFactory(rounded : Bool) -> AbstractFactory {
if rounded {
return RoundedShapeFactory()
}else {
return ShapeFactory()
}
}
}
5. 데모
let shapeFactory1 = FactoryProducer.getFactory(rounded: false)
let shape1 = shapeFactory1.getShape(shapeType: "RECTANGLE")
shape1?.draw()
let shape2 = shapeFactory1.getShape(shapeType: "SQUARE")
shape2?.draw()
let shapeFactory2 = FactoryProducer.getFactory(rounded: true)
let shape3 = shapeFactory2.getShape(shapeType: "RECTANGLE")
shape3?.draw()
let shape4 = shapeFactory2.getShape(shapeType: "SQUARE")
shape4?.draw()
6. 출력 결과
Inside Rectangle::draw() method
Inside Square::draw() method
Inside RoundedRectangle::draw() method
Inside RoundedSquare::draw() method
References
'Computer Science > 디자인 패턴' 카테고리의 다른 글
[디자인 패턴] MVP, MVVM 패턴(MVC와의 차이) (0) | 2023.01.13 |
---|---|
[디자인 패턴] MVC 패턴(Model-View-Controller Pattern) (0) | 2023.01.13 |
[디자인 패턴] 프록시 패턴(Proxy Pattern) (0) | 2023.01.12 |
[디자인 패턴] 싱글톤 패턴(Singleton Pattern) (0) | 2023.01.08 |