Problem 제시 다양한 고객을 대상으로 영업하는 A씨는 자신 만의 고객관리가 필요함을 느꼈다. 초기 디자인에는 A씨 고객이 주로 국내인으로 되어 있어서 아래와 같은 형태로 개발되었으나 영업을 하면서 외국에 거주하는 고객이 점차 늘어나게 되었고, A씨는 주소와 전화번호가 거주하는 국가에 맞게 생성되기를 원한다.
package abstractFactory;
public class Client {
public static void main(String[] args) {
System.out.println("국내 거주 주소 및 전화번호를 가져온다.");
Client client = new Client();
KRAddress krAddress = client.createAddress("KR");
krAddress.setPostalCode("123-456");
krAddress.setCountry("대한민국");
krAddress.setCity("서울");
krAddress.setRegion("강남구");
krAddress.setAddr("좋은아파트 31-1102");
KRPhoneNumber krPhoneNumber = client.createPhoneNumber("KR");
krPhoneNumber.setCountryCode("82");
krPhoneNumber.setPhoneNumber("02-555-7282");
System.out.println("주소 : " + krAddress.getFullAddress());
System.out.println("전화번호 : ("+ krPhoneNumber.getCountryCode() + ") "
+ krPhoneNumber.getPhoneNumber());
}
/**
* 주소를 가져온다
* @param countryType
* @return krAddress
*/
private KRAddress createAddress(String countryType) {
KRAddress krAddress = null;
if (countryType.equals("KR")) {
krAddress = new KRAddress();
}
return krAddress;
}
/**
* 전화번호를 가져온다
* @param countryType
* @return krPhoneNumber
*/
private KRPhoneNumber createPhoneNumber(String countryType) {
KRPhoneNumber krPhoneNumber = null;
if (countryType.equals("KR")) {
krPhoneNumber = new KRPhoneNumber();
}
return krPhoneNumber;
}
}
package abstractFactory;
public class KRAddress {
private String postalCode;
private String country;
private String city;
private String region;
private String addr;
public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}
public void setCountry(String country) {
this.country = country;
}
public void setCity(String city) {
this.city = city;
}
public void setRegion(String region) {
this.region = region;
}
public void setAddr(String addr) {
this.addr = addr;
}
public String getFullAddress() {
return postalCode +" " + country +" " + city +" " + region +" " + addr;
}
}
package abstractFactory;
public class KRPhoneNumber {
private String countryCode;
private String phoneNumber;
public String getCountryCode() {
return countryCode;
}
public void setCountryCode(String countryCode) {
this.countryCode = countryCode;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
[실행결과]
솔루션 찾기 (문제해결 실습) 추상화 또는 일반화 하기 -. 미국에 거주하는 고객이 추가되었을 경우 추상화 또는 일반화 시켜야 할 부분은 어디인지 클래스 다이어그램으로 표현하시오. -. 클래스 다이어그램에 적용한 사항을 실행하여 제대로 동작하는지 여부를 확인하시오. 변경의 국지화 (Localization of Changes) -. 변경이 예상되는 부분을 국지화시킴으로써 비용을 최소화 할 수 있다.
변경을 국지화 할 수 있는 부분을 찾아 다이어그램으로 표현하시오.
-. 자신의 다이어그램을 옆에 있는 사람과 비교 토의하시오.
솔루션 발표 문제해결을 위해 디자인한 다이어그램과 개발 소스를 기준으로 발표
패턴 소개 - Abstract Factory 패턴
의도 구체적인 클래스를 지정하지 않고 관련성을 갖는 개체들의 집합을 생성함. 서로 독립적인 객체들의 집합을 생성할 수 있는 인터페이스를 제공함.
동기 Problem을 통해서 나타난 문제점을 Abstract Factory 패턴을 통해 그 대안을 찾고자 한다
추상화 결정하기
변경의 국지화 (Localization of Changes)
추상화 결정하기
이미 국내에 거주하는 고객 주소, 전화번호에 대해서는 잘 정의되어 있음.
하지만, 다양한 형태의 고객 주소, 전화번호 변화를 가지기 위해서는 추상클래스를 도출해야 함.
단, 각각의 전화번호의 국가코드는 하위 클래스 유형에 따라 결정되며 이를 정적인 형태로 정의해야 함.
package abstractFactory;
public class Client {
public static void main(String[] args) {
System.out.println("국내 거주 주소 및 전화번호를 가져온다.");
Client client = new Client();
Address Address = client.createAddress("KR");
Address.setPostalCode("123-456");
Address.setCountry("대한민국");
Address.setCity("서울");
Address.setRegion("강남구");
Address.setAddr("좋은아파트 31-1102");
PhoneNumber phoneNumber = client.createPhoneNumber("KR");
phoneNumber.setCountryCode("82");
phoneNumber.setPhoneNumber("02-555-7282");
System.out.println("주소 : " + Address.getFullAddress());
System.out.println("전화번호 : ("+ phoneNumber.getCountryCode() + ") "
+ phoneNumber.getPhoneNumber());
}
/**
* 주소를 가져온다
* @param countryType
* @return address
*/
private Address createAddress(String countryType) {
Address address = null;
if (countryType.equals("KR")) {
address = new KRAddress();
} else if (countryType.equals("US")) {
address = new USAddress();
}
return address;
}
/**
* 전화번호를 가져온다
* @param countryType
* @return phoneNumber
*/
private PhoneNumber createPhoneNumber(String countryType) {
PhoneNumber phoneNumber = null;
if (countryType.equals("KR")) {
phoneNumber = new KRPhoneNumber();
}else if (countryType.equals("US")) {
phoneNumber = new USPhoneNumber();
}
return phoneNumber;
}
}
package abstractFactory;
public abstract class Address {
private String postalCode;
private String country;
private String city;
private String region;
private String addr;
public String getPostalCode() {
return postalCode;
}
public String getCountry() {
return country;
}
public String getCity() {
return city;
}
public String getRegion() {
return region;
}
public String getAddr() {
return addr;
}
public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}
public void setCountry(String country) {
this.country = country;
}
public void setCity(String city) {
this.city = city;
}
public void setRegion(String region) {
this.region = region;
}
public void setAddr(String addr) {
this.addr = addr;
}
public abstract String getFullAddress() ;
}
package abstractFactory;
public abstract class PhoneNumber {
private String countryCode;
private String phoneNumber;
public abstract String getCountryCode();
public void setCountryCode(String countryCode) {
this.countryCode = countryCode;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
package abstractFactory;
public class KRPhoneNumber extends PhoneNumber {
private static final String COUNTRY_CODE = "82";
@Override
public String getCountryCode() {
return COUNTRY_CODE;
}
}
package abstractFactory;
public class USPhoneNumber extends PhoneNumber {
private static final String COUNTRY_CODE = "01";
@Override
public String getCountryCode() {
return COUNTRY_CODE;
}
}
변경의 국지화 (Localization of Changes)
앞서 추상화시킨 클래스의 인스턴스는 어떻게 생성할 것인가? 클라이언트에서 인스턴스를 생성한다면 유지보수의 문제가 발생함.
private Address createAddress(String countryType) {
Address address = null;
if (countryType.equals("KR")) {
address = new KRAddress();
} else if (countryType.equals("US")) {
address = new USAddress();
} else if (countryType.equals("FR")) {
address = new FRAddress();
}
return address;
}
private PhoneNumber createPhoneNumber(String countryType) {
PhoneNumber phoneNumber = null;
if (countryType.equals("KR")) {
phoneNumber = new KRPhoneNumber();
}else if (countryType.equals("US")) {
phoneNumber = new USPhoneNumber();
} else if (countryType.equals("FR")) {
phoneNumber = new FRPhoneNumber();
}
return phoneNumber;
}
새로운 오브젝트를 추가해야 할 경우 클라이언트소스의 여러 곳에 영향을 미침.
그래서 새로운 인스턴스를 생성하기 위한 Factory 객체를 사용해야 함.
package abstractFactory;
public interface AddressFactory {
public abstract Address createAddress();
public abstract PhoneNumber createPhoneNumber();
}
package abstractFactory;
public class KRAddressFactory implements AddressFactory {
@Override
public Address createAddress() {
return new KRAddress();
}
@Override
public PhoneNumber createPhoneNumber() {
return new KRPhoneNumber();
}
}
package abstractFactory;
public class USAddressFactory implements AddressFactory{
@Override
public Address createAddress() {
return new USAddress();
}
@Override
public PhoneNumber createPhoneNumber() {
return new USPhoneNumber();
}
}
package abstractFactory;
public class Client {
public static void main(String[] args) {
Client client = new Client();
Address krAddress = client.createAddress("KR");
krAddress.setPostalCode("123-456");
krAddress.setCountry("대한민국");
krAddress.setCity("서울");
krAddress.setRegion("강남구");
krAddress.setAddr("좋은아파트 31-1102");
PhoneNumber krPhoneNumber = client.createPhoneNumber("KR");
krPhoneNumber.setPhoneNumber("02-555-7282");
Address usAddress = client.createAddress("US");
usAddress.setPostalCode("22554");
usAddress.setCountry("UNITED STATES");
usAddress.setCity("STANFORD");
usAddress.setRegion("AQUIA DR VA");
usAddress.setAddr("2125");
PhoneNumber usPhoneNumber = client.createPhoneNumber("US");
usPhoneNumber.setPhoneNumber("714.555.1234");
System.out.println("국내 거주 주소 및 전화번호를 가져온다.");
System.out.println("주소 : " + krAddress.getFullAddress());
System.out.println("전화번호 : ("+ krPhoneNumber.getCountryCode() + ") "
+ krPhoneNumber.getPhoneNumber());
System.out.println();
System.out.println("해외 거주 주소 및 전화번호를 가져온다.");
System.out.println("Address : " + usAddress.getFullAddress());
System.out.println("PhoneNumber : ("+ usPhoneNumber.getCountryCode() + ") "
+ usPhoneNumber.getPhoneNumber());
}
public Address createAddress(String countryType) {
AddressFactory addressFactory = getAddressFactory(countryType);
return addressFactory.createAddress();
}
public PhoneNumber createPhoneNumber(String countryType) {
AddressFactory addressFactory = getAddressFactory(countryType);
return addressFactory.createPhoneNumber();
}
private AddressFactory getAddressFactory(String countryType) {
if (countryType.equals("KR")) return new KRAddressFactory();
else if (countryType.equals("US")) return new USAddressFactory();
return null;
}
}
[실행 결과]
적용
- 생성되고 구성되고 표현되는 방식과 무관하게 시스템을 독립적으로 만들고자 할 때
- 하나 이상의 제품군들 중 하나를 선택해서 시스템을 설정해야 하고 한번 구성한 제품을 다른 것으로 대체할 수 있을 때
- 관련된 객체군을 함께 사용해서 시스템을 설계하고, 이 제품이 갖는 제약 사항을 따라야 할 때
- 제품에 대한 클래스 라이브러리를 제공하고, 그들의 구현이 아닌 인터페이스를 표현하고 싶을 때