객체지향 디자인 패턴 2편
Facade, Template-method, Decorator, Factory-method, Abstract-factory-method, Mediator, Composite
지난 번 여섯가지 패턴들에 이어
이번 영상에서는 일곱가지 패턴들을 더 알아봅니다.
패턴 | 분:초 |
---|---|
Facade 패턴 | 00:10 |
Template-method 패턴 | 01:32 |
Decorator | 04:07 패턴 |
Factory-method 패턴 | 06:07 |
Abstract-factory-method 패턴 | 09:00 |
Mediator 패턴 | 09:38 |
Composite 패턴 | 12:09 |
❗각 패턴의 내용들이 서로 연결된 부분이 있기 때문에
가능한 전체 영상을 시청할 것을 권장합니다.
예제 코드들을 아래에서 각각 확인하세요 😊
Facade 패턴
패턴이란 인식 없이 널리 사용되고 있는,
가장 단순하다고 할 수 있는 패턴입니다.
FacadePattern.java
import java.util.Map;
public class FacadePattern {
public static void main(String[] args) {
double[] myGeoLoc = new GeoLocation().getGeoLoc();
InternetConnection conn = new InternetConnection();
conn.connect();
String data = conn.getData("https://주소_API_URL", myGeoLoc);
conn.disconnect();
Map<String, Object> address = new Json().parse(data);
System.out.println(address.get("address"));
// 서울시 개발구 객체동
new MyLocFacade().printMyAddress();
}
}
GeoLocation.java
public class GeoLocation {
public double[] getGeoLoc() {
double[] geoLoc = {0, 0};
return geoLoc;
}
}
InternetConnection.java
public class InternetConnection {
public void connect() {};
public String getData(String url, Object param) {
return "";
}
public void disconnect() {};
}
Json.java
import java.util.HashMap;
import java.util.Map;
public class Json {
public Map<String, Object> parse(String str) {
Map<String, Object> result = new HashMap<>();
result.put("address", "서울시 개발구 객체동");
return result;
}
}
MyLocFacade.java
import java.util.Map;
public class MyLocFacade {
public void printMyAddress () {
double[] myGeoLoc = new GeoLocation().getGeoLoc();
InternetConnection conn = new InternetConnection();
conn.connect();
String data = conn.getData("https://주소_API_URL", myGeoLoc);
conn.disconnect();
Map<String, Object> address = new Json().parse(data);
System.out.println(address.get("address"));
}
}
Template method 패턴
전체 절차는 정해져 있지만
세부 절차를 자식 클래스로 다양화할 수 있는 패턴입니다.
TemplateExample.java
public class TemplateExample {
public static void main(String[] args) {
new NaverMapView().initMap();
new KakaoMapView().initMap();
}
}
MapView.java
public abstract class MapView {
protected abstract void connectMapServer();
protected abstract void showMapOnScreen();
protected abstract void moveToCurrentLocation();
public void initMap () {
connectMapServer();
showMapOnScreen();
moveToCurrentLocation();
}
}
NaverMapView.java
public class NaverMapView extends MapView {
@Override
protected void connectMapServer() {
System.out.println("네이버 지도 서버에 연결");
};
@Override
protected void showMapOnScreen() {
System.out.println("네이버 지도를 보여줌");
};
@Override
protected void moveToCurrentLocation() {
System.out.println("네이버 지도에서 현 좌표로 이동");
};
}
KakaoMapView.java
public class KakaoMapView extends MapView {
@Override
protected void connectMapServer() {
System.out.println("카카오 지도 서버에 연결");
};
@Override
protected void showMapOnScreen() {
System.out.println("카카오 지도를 보여줌");
};
@Override
protected void moveToCurrentLocation() {
System.out.println("카카오 지도에서 현 좌표로 이동");
};
}
데코레이터 패턴
객체를 다른 객체에 넣어 기능을 추가하는 방식
바로 이해하기 낯설지만 잘 활용하면 정말 유용한 패턴입니다.
DecoratorPattern.java
public class DecoratorPattern {
public static void main(String[] args) {
new XWingFighter().attack();
// 탄환 발사
new LaserDecorator(new XWingFighter()).attack();
// 탄환 발사
// 레이저 발사
new PlasmaDecorator(
new MissileDecorator(
new LaserDecorator(
new XWingFighter()
))).attack();
// 탄환 발사
// 레이저 발사
// 미사일 발사
// 플라즈마 발사
}
}
Fighter.java
public interface Fighter {
public void attack ();
}
XWingFighter.java
public class XWingFighter implements Fighter {
@Override
public void attack () {
System.out.println("탄환 발사");
}
}
FighterDecorator.java
public abstract class FighterDecorator implements Fighter {
private Fighter decoratedFighter;
public FighterDecorator(Fighter _decoratedFighter) {
decoratedFighter = _decoratedFighter;
}
@Override
public void attack () {
decoratedFighter.attack();
}
}
LaserDecorator.java
public class LaserDecorator extends FighterDecorator {
public LaserDecorator (Fighter _decoratedFighter) {
super(_decoratedFighter);
}
@Override
public void attack () {
super.attack();
System.out.println("레이저 발사");
}
}
MissileDecorator.java
public class MissileDecorator extends FighterDecorator {
public MissileDecorator (Fighter _decoratedFighter) {
super(_decoratedFighter);
}
@Override
public void attack () {
super.attack();
System.out.println("미사일 발사");
}
}
PlasmaDecorator.java
public class PlasmaDecorator extends FighterDecorator {
public PlasmaDecorator (Fighter _decoratedFighter) {
super(_decoratedFighter);
}
@Override
public void attack () {
super.attack();
System.out.println("플라즈마 발사");
}
}
Factory method 패턴
클래스를 선택하고 객체를 고르는 일은 이제 공장에 맡기세요!
기본 예제
FactoryMethod.java
class FactoryMethod {
public static void main(String[] args) {
new Console().withoutFactory();
new Console().withFactory();
}
}
Component.java
abstract class Component {
protected abstract String getCompName ();
public Component () {
System.out.println(this.getCompName() + " 생성");
}
}
class Button extends Component {
@Override
protected String getCompName() { return "버튼"; }
}
class Switch extends Component {
@Override
protected String getCompName() { return "스위치"; }
}
class Dropdown extends Component {
@Override
protected String getCompName() { return "드랍다운"; }
}
CompFactory.java
class CompFactory {
public Component getComp (Usage usage) {
if (usage == Usage.PRESS) {
return new Button();
} else if (usage == Usage.TOGGLE) {
return new Switch();
} else {
return new Dropdown();
}
}
}
Console.jafa
class Console {
private CompFactory compFactory = new CompFactory();
Component comp1;
Component comp2;
Component comp3;
void withoutFactory () {
comp1 = new Button();
comp2 = new Switch();
comp3 = new Dropdown();
}
void withFactory () {
comp1 = compFactory.getComp(Usage.PRESS);
comp2 = compFactory.getComp(Usage.TOGGLE);
comp3 = compFactory.getComp(Usage.EXPAND);
}
}
enum Usage {
PRESS, TOGGLE, EXPAND
}
Decorator 패턴 예제에 적용
FighterFactory.java
public class FighterFactory {
public Fighter getFighter(boolean laser, boolean missile, boolean plasma) {
Fighter fighter = new XWingFighter();
if (laser) fighter = new LaserDecorator(fighter);
if (missile) fighter = new MissileDecorator(fighter);
if (plasma) fighter = new PlasmaDecorator(fighter);
return fighter;
}
}
FactoryDecorator.java
public class FactoryDecorator {
public static void main(String[] args) {
FighterFactory factory = new FighterFactory();
factory.getFighter(false, false, false).attack();
// 탄환 발사
factory.getFighter(true, false, true).attack();
// 탄환 발사
// 레이저 발사
// 플라즈마 발사
factory.getFighter(true, true, false).attack();
// 탄환 발사
// 레이저 발사
// 미사일 발사
factory.getFighter(true, true, true).attack();
// 탄환 발사
// 레이저 발사
// 미사일 발사
// 플라즈마 발사
}
}
Abstract factory 패턴
공장을 추상화하여 다양화하기 위한 패턴입니다.
FactoryMethod.java
class FactoryMethod {
public static void main(String[] args) {
new Console().withFactory();
}
}
Component.java
abstract class Component {
protected abstract String getCompName ();
public Component () {
System.out.println(this.getCompName() + " 생성");
}
}
class LightButton extends Component {
@Override
protected String getCompName() { return "라이트 버튼"; }
}
class DarkButton extends Component {
@Override
protected String getCompName() { return "다크 버튼"; }
}
class LightSwitch extends Component {
@Override
protected String getCompName() { return "라이트 스위치"; }
}
class DarkSwitch extends Component {
@Override
protected String getCompName() { return "다크 스위치"; }
}
class LightDropdown extends Component {
@Override
protected String getCompName() { return "라이트 드랍다운"; }
}
class DarkDropdown extends Component {
@Override
protected String getCompName() { return "다크 드랍다운"; }
}
CompFactory.java
interface CompFactory {
public Component getComp (Usage usage);
}
// 라이트 테마 공장
class LightCompFactory implements CompFactory {
@Override
public Component getComp (Usage usage) {
if (usage == Usage.PRESS) {
return new LightButton();
} else if (usage == Usage.TOGGLE) {
return new LightSwitch();
} else {
return new LightDropdown();
}
}
}
// 다크 테마 공장
class DarkCompFactory implements CompFactory {
@Override
public Component getComp (Usage usage) {
if (usage == Usage.PRESS) {
return new DarkButton();
} else if (usage == Usage.TOGGLE) {
return new DarkSwitch();
} else {
return new DarkDropdown();
}
}
}
Console.java
class Console {
private CompFactory lightCompFactory = new LightCompFactory();
private CompFactory darkCompFactory = new DarkCompFactory();
Component comp1;
Component comp2;
Component comp3;
void withFactory () {
comp1 = lightCompFactory.getComp(Usage.PRESS);
comp2 = lightCompFactory.getComp(Usage.TOGGLE);
comp3 = lightCompFactory.getComp(Usage.EXPAND);
// 라이트 버튼 생성
// 라이트 스위치 생성
// 라이트 드랍다운 생성
comp1 = darkCompFactory.getComp(Usage.PRESS);
comp2 = darkCompFactory.getComp(Usage.TOGGLE);
comp3 = darkCompFactory.getComp(Usage.EXPAND);
// 다크 버튼 생성
// 다크 스위치 생성
// 다크 드랍다운 생성
}
}
enum Usage {
PRESS, TOGGLE, EXPAND
}
미디에이터 패턴
한 클래스에서의 이벤트가 연결된 다른 클래스의 객체에 영향을 미칠 때
미디에이터 패턴으로 효율적인 설계를 할 수 있습니다.
MediatorPattern.java
public class MediatorPattern {
public static void main(String[] args) {
ModeSwitch modeSwitch = new ModeSwitch();
ModeMediator modeMediator = new ModeMediator();
modeSwitch.setModeMediator(modeMediator);
modeMediator.addListener(new ListView());
modeMediator.addListener(new GalleryView());
modeMediator.addListener(new DataDownloader());
modeSwitch.toggleMode();
// 리스트뷰 감춤
// 갤러리뷰 보여줌
// 갤러리뷰용 데이터 다운로드
modeSwitch.toggleMode();
// 리스트뷰 보여줌
// 갤러리뷰 감춤
// 리스트뷰용 데이터 다운로드
}
}
ModeSwitch.java
public class ModeSwitch {
Mode mode = Mode.LIST;
ModeMediator modeMediator;
public void setModeMediator (ModeMediator _modeMediator) {
modeMediator = _modeMediator;
}
public void toggleMode () {
mode = mode == Mode.LIST ? Mode.GALLERY : Mode.LIST;
if (modeMediator != null) {
modeMediator.onModeChange(mode);
}
}
}
enum Mode { LIST, GALLERY }
ModeListener.java
public interface ModeListener {
public void onModeChange (Mode mode);
}
class ListView implements ModeListener {
@Override
public void onModeChange(Mode mode) {
System.out.println(
"리스트뷰 " + (mode == Mode.LIST ? "보여줌" : "감춤")
);
}
}
class GalleryView implements ModeListener {
@Override
public void onModeChange(Mode mode) {
System.out.println(
"갤러리뷰 " + (mode == Mode.GALLERY ? "보여줌" : "감춤")
);
}
}
class DataDownloader implements ModeListener {
@Override
public void onModeChange(Mode mode) {
System.out.println(
(mode == Mode.LIST ? "리스트" : "갤러리")
+ "뷰용 데이터 다운로드");
}
}
ModeMediator.java
import java.util.ArrayList;
public class ModeMediator {
ArrayList<ModeListener> listeners = new ArrayList<>();
public void addListener(ModeListener listener) {
listeners.add(listener);
}
public void onModeChange (Mode mode) {
for (ModeListener listener : listeners) {
listener.onModeChange(mode);
}
}
}
Composite 패턴
특정 클래스의 객체들을 트리 관계로 다루고
포함하는 객체와 포함되는 객체를 같은 인터페이스로
다룰 수 있도록 하는 패턴입니다.
FileSystem.java
import java.util.ArrayList;
public interface FileSystem {
public int getSize();
public void remove();
}
class File implements FileSystem {
private String name;
private int size;
public File (String _name, int _size) {
name = _name; size = _size;
}
@Override
public int getSize() {
System.out.println(name + "파일 크기 : " + size);
return size;
}
@Override
public void remove() {
System.out.println(name + " 파일 삭제");
}
}
class Folder implements FileSystem {
private String name;
private ArrayList<FileSystem> includeds = new ArrayList<>();
public Folder (String _name) {
name = _name;
}
public void add(FileSystem fileSystem) {
includeds.add(fileSystem);
}
@Override
public int getSize() {
int total = 0;
for (FileSystem included : includeds) {
total += included.getSize();
}
System.out.println(name + "폴더 크기 : " + total);
System.out.println("- - - - -");
return total;
}
@Override
public void remove() {
for (FileSystem included : includeds) {
included.remove();
}
System.out.println(name + " 폴더 삭제");
System.out.println("- - - - -");
}
}
CompositePattern.java
public class CompositePattern {
public static void main(String[] args) {
Folder schoolFolder = new Folder("학교");
Folder grade1Folder = new Folder("1학년");
Folder grade2Folder = new Folder("2학년");
schoolFolder.add(grade1Folder);
schoolFolder.add(grade2Folder);
File enterPhoto = new File("입학사진", 256);
grade1Folder.add(enterPhoto);
Folder sem1Folder = new Folder("1학기");
Folder sem2Folder = new Folder("2학기");
grade2Folder.add(sem1Folder);
grade2Folder.add(sem2Folder);
File lecturePlan = new File("강의계획서", 120);
sem1Folder.add(lecturePlan);
Folder projFolder = new Folder("프로젝트");
sem2Folder.add(projFolder);
File draft = new File("드래프트", 488);
File finalResult = new File("결과물", 560);
projFolder.add(draft);
projFolder.add(finalResult);
schoolFolder.getSize();
// 입학사진파일 크기 : 256
// 1학년폴더 크기 : 256
// - - - - -
// 강의계획서파일 크기 : 120
// 1학기폴더 크기 : 120
// - - - - -
// 드래프트파일 크기 : 488
// 결과물파일 크기 : 560
// 프로젝트폴더 크기 : 1048
// - - - - -
// 2학기폴더 크기 : 1048
// - - - - -
// 2학년폴더 크기 : 1168
// - - - - -
// 학교폴더 크기 : 1424
// - - - - -
schoolFolder.remove();
// 입학사진 파일 삭제
// 1학년 폴더 삭제
// - - - - -
// 강의계획서 파일 삭제
// 1학기 폴더 삭제
// - - - - -
// 드래프트 파일 삭제
// 결과물 파일 삭제
// 프로젝트 폴더 삭제
// - - - - -
// 2학기 폴더 삭제
// - - - - -
// 2학년 폴더 삭제
// - - - - -
// 학교 폴더 삭제
// - - - - -
}
}
🍿 더 자세한 내용은 영상에서 보실 수 있습니다.