객체의 인스턴스를 만드는 작업이 항상 공개되어 있어야 하는 것은 아니며,
모든 것을 공개했다가는 결합과 관련된 문제가 생길 수 있다는 것을 배우게 된다.
자바에서 "new"는 "구상 객체"를 뜻합니다.
- 뭐가 변화되는 것 때문에 new를 사용하는데 있어서 조심해야 합니다.
- 인터페이스를 바탕으로 코드를 만들면, 다형성 덕분에 어떤 클래스든 특정 인터페이스만 구현하면 사용할 수 있기 때문이죠.
반대로 코드에서 구상 클래스를 많이 사용하면새로운 구상클래스가 추가 될 때마다 코드를 고쳐야 합니다.
구상클래스 : Car car = new Car(); -> new를 사용하여 만듬
인터페이스 : Duck duck = new MallardDuck(); -> Duck == 인터페이스
.
.
.
가정) 피자가게로 팩토리패턴의 예시를 만들겠습니다.
1 단계
//피자 만드는 메소드
Pizza orderPizza() {
Pizza pizza = new Pizza();
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
-->
2 단계
//피자 종류가 많은 경우
Pizza orderPizza(String type) {
Pizza pizza = new Pizza();
if(type.equals("cheese")) {
pizza = new CheesePizza();
} else if(type.equals("pepperoni")) {
pizza = new PepperoniPizza();
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
-->
3 단계
만약 종류가 추가되면?.. 코드를 변경해줘야한다(바뀌는 부분)
팩토리 패턴 : 객체 생성을 처리하는 클래스
.
.
.
간단한 피자 팩토리를 만들어 보겠습니다.
//피자 객체를 만드는 팩토리 패턴
public class SimplePizzaFactory {
Pizza createPizza(String type) {
Pizza pizza = null;
if(type.equals("cheese")) {
pizza = new CheesePizza();
} else if(type.equals("pepperoni")) {
pizza = new PepperoniPizza();
}
return pizza;
}
}
// 피자가게 클래스
public class PizzaStore {
//참조 저장
SimplePizzaFactory simplePizzaFactory;
public PizzaStore(SimplePizzaFactory factory) {
this.simplePizzaFactory = factory;
}
public Pizza orderPizza(String type) {
Pizza pizza;
//피자 팩토리 내 메소드
pizza = simplePizzaFactory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
.
.
.
가정) 피자가게가 큰 성공을 거둬서 인기를 끌게 되어 다른 지역에서도 프랜차이즈 사업을 시작하려합니다.
하지만, 지역별로 조금씩 다른 차이점을 어떤식으로 적용해야 할까요?
//뉴욕피자
NewYorkFactory NYfactory = new NewYorkFactory();
PizzaStore nyStore = new PizzaStore(NYfactory);
nyStore.order("Cheese")
//시카고피자
chicagoFactory chicagofactory = new chicagoFactory();
PizzaStore chicagoStore = new PizzaStore(chicagofactory);
chicagoStore.order("Cheese")
.
.
.
가정) 위의 방법을 직접 적용해 봤습니다. 하지만,
분점에서 굽는 방식이 달라진다거나 종종 피자를 자르는 것을 까먹어 버리는 일이 일어나기 시작했습니다.
pizza.cut()을 사용하지 않는다거나...
해서, 피자 가게와 피자 제작 과정 전체를 한개로 묶어주는 프레임워크를 만들어야 되겠다는 결론에 도달했습니다.
.
.
.
피자가게 프레임워크
createPizza() 메소드를 추상메소드로 선언하고,
각 지역마다 고유의 스타일에 맞게 PizzaStore의 서브클래스를 만들도록 할 것입니다.
// abstract : 팩토리 메소드는 추상 메소드로 선언하면 서브클래스에서 객체 생성을 책임지도록 합니다.
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza;
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
// 추상메소드
abstract Pizza createPizza(String type);
}
//NYPizzaStore은 PizzaStore을 확장하기 때문에 orderPizza()메소드도 자동으로 상속받게 됩니다.
public class NYPizzaStore extends PizzaStore{
@Override
Pizza createPizza(String item) {
if (item.equals("cheese")) {
return new NYCheesePizza();
} else if(item.equals("pepperoni")) {
return new NYPepperoniPizza();
} else {
return null;
}
}
}
* 서브클래스가 만들어지기 전까지는 orderPizza() 메소드는 구현되지 않은 상태입니다.(추상클래스이니까요.)
* 슈퍼클래스에 있는 orderPizza() 메소드에서는 어떤 피자가 만들어지는지 전혀 알 수 없다는 점을 잘 이해해야 합니다.
그 메소드에서는 피자를 준비하고 굽고, 자르고, 포장하는 작업을 처리할 뿐입니다.
즉, PizzaStore와 Pizza는 완전히 분리되어 있습니다.
.
.
.
피자가 주문에따라 만들어지는 과정을 한번 살펴보도록 하겠습니다.
1. 뉴욕피자가게 객체생성
PizzaStore nyPizzaStore = new NYPizzaStore();
2. 주문 받기
nyPizzaStore.orderPizza("cheese");
3. orderPizza()메소드에서 creatPizza() 메소드를 호출하면, 피자의 type안에 cheese가 들어가겠죠?
Pizza pizza = createPizza("cheese");
.
.
.
전체적인 자바 코드를 만들도록 하겠습니다.
//피자 추상클래스
public abstract class Pizza {
String name;
String dough;
String sause;
ArrayList toppings = new ArrayList();
void prepare() {
Log.d("abcd","준비 : "+name);
Log.d("abcd","도우 준비, 소스 추가, 토핑 추가");
for (int i = 0 ; i < toppings.size(); i++) {
Log.d("abcd",toppings.get(i).toString());
}
}
void bake() {
Log.d("abcd","반죽");
}
void cut() {
Log.d("abcd","자르기");
}
void box() {
Log.d("abcd","포장");
}
public String getName() {
return name;
}
}
//뉴욕치즈피자 구현클래스
public class NYCheesePizza extends Pizza{
public NYCheesePizza() {
name = "뉴욕 치즈 피자";
dough = "뉴욕 치즈 피자 도우";
sause = "뉴욕 치즈 피자 소스;";
toppings.add("잘게 썬 치즈");
}
}
//피자가게 추상클래스
public abstract class PizzaStore {
//피자 주문 메소드
public Pizza orderPizza(String type) {
Pizza pizza;
//피자 팩토리 내 메소드
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
//추상 메소드로서 PizzaStore 클래스를 상속하는 서브클래스에서 구현한다.
abstract Pizza createPizza(String type);
}
//뉴욕에 있는 피자가게 구현클래스
public class NYPizzaStore extends PizzaStore{
@Override
Pizza createPizza(String item) {
if (item.equals("cheese")) {
return new NYCheesePizza();
} else if(item.equals("pepperoni")) {
return new NYPepperoniPizza();
} else {
return null;
}
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
PizzaStore pizzaStore = new NYPizzaStore();
//pizzaStore.orderPizza() 메소드는 pizza를 리턴한다.
Pizza pizza = pizzaStore.orderPizza("cheese");
Log.d("abcd","what pizza : "+pizza.getName());
}
}
결과 : D/abcd: 준비 : 뉴욕 치즈 피자
도우 준비, 소스 추가, 토핑 추가
잘게 썬 치즈
반죽
자르기
포장
what pizza : 뉴욕 치즈 피자
.
.
팩토리 메소드 패턴 : 팩토리 메소드 패턴에서는 객체를 생성하기 위한 인터페이스를 정의하는데,
어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정하게 만듭니다.
.
.
디자인 원칙 : 추상화된 것에 의존하도록 만들어라. 구성 클래스에 의존하도록 만들지 않도록 한다.
의미 : 고수준 구성요소가 저수준 구성요소에 의존하면 안된다는 것이 내포되어 있습니다.
예를 들어, PizzaStore클래스의 행동은 피자에 의해 정의되기 때문에, PizzaStore클래스는 고수준 구성요소라고 할 수 있습니다.
PizzaStore클래스에서는 다양한 피자객체를 만들고, 피자를 준비하고, 굽는 등의 행동을합니다.
이때, PizzaStore에서 사용하는 pizza 객체들은 저수준 구성요소라고 할 수 있습니다.
.
.
정리
코틀린
//피자 추상클래스
abstract class Pizza {
var name:String? = null
var dough:String? = null
var sause:String? = null
var toppings:ArrayList<String>? = null
open fun prepare() {
Log.d("abcd","준비 : "+name)
Log.d("abcd","도우 준비, 소스 추가, 토핑 추가")
for (i in 0 until toppings?.size!!) {
toppings?.get(i).toString()!!
}
}
open fun bake() {
Log.d("abcd", "반죽")
}
open fun cut() {
Log.d("abcd", "자르기")
}
open fun box() {
Log.d("abcd", "포장")
}
}
//피자 구현클래스
class NYCheesePizza : Pizza() {
init {
name = "뉴욕 치즈 피자";
dough = "뉴욕 치즈 피자 도우";
sause = "뉴욕 치즈 피자 소스;";
//todo 객체 생성 arrayList()
toppings = ArrayList()
toppings?.add("잘게 썬 치즈")
}
}
//피자가게 추상클래스
abstract class PizzaStore {
fun orderPizza(type:String) : Pizza {
var pizza: Pizza?
pizza = createPizza(type)
//준비, 반죽 등
pizza.prepare()
pizza.bake()
pizza.cut()
pizza.box()
return pizza!!
}
abstract fun createPizza(type: String) : Pizza
}
//피자가게 구현클래스
class NYPizzaStore : PizzaStore() {
override fun createPizza(type: String): Pizza {
if (type == "cheese") {
return NYCheesePizza()
} else if(type == "pepperoni") {
return NYPepperoniPizza()
} else {
return NYCheesePizza()
}
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val pizzaStore = NYPizzaStore()
val pizza = pizzaStore.orderPizza("cheese")
Log.d("abcd", "pizza name is : " + pizza.name)
}
}
결과 :
D/abcd: 준비 : 뉴욕 치즈 피자
도우 준비, 소스 추가, 토핑 추가
D/abcd: 반죽
자르기
포장
pizza name is : 뉴욕 치즈 피자
다음글에서 팩토리패턴에 interface를 추가하는 글을 봐주세요!
'개발공부 > 디자인 패턴' 카테고리의 다른 글
자바 및 코틀린 싱글톤(singleton) 패턴이란? (0) | 2022.01.21 |
---|---|
자바 및 코틀린 추상 팩토리(factory)패턴이란? (0) | 2022.01.19 |
자바 및 코틀린 객체지향패턴 데코레이터(decorator)패턴 (0) | 2022.01.10 |
자바 및 코틀린 객체지향패턴 (Observer 패턴) (0) | 2022.01.06 |
자바 및 코틀린 객체지향패턴 (위임, 인터페이스 사용법) (0) | 2022.01.03 |