객체지향 디자인 패턴 1편
Singleton, Strategy, State, Command, Adapter, Proxy
객체지향 언어로 프로젝트를 진행할 때 익혀두면 유용할 패턴들.
영상에서 여섯가지를 패턴들을 알아보겠습니다.
패턴 | 분:초 |
---|---|
Singleton 패턴 | 01:01 |
Strategy 패턴 | 03:52 |
State 패턴 | 06:48 |
Command 패턴 | 08:19 |
Adapter 패턴 | 11:09 |
Proxy 패턴 | 13:52 |
❗각 패턴의 내용들이 서로 연결된 부분이 있기 때문에
가능한 전체 영상을 시청할 것을 권장합니다.
예제 코드들을 아래에서 각각 확인하세요 😊
Singleton 패턴
프로세스에 특정 객체가 하나만
존재해야 할 때 쓰는 패턴이죠.
화면들에 공유되는 앱 세팅을 싱글턴으로 설정해보는 예제입니다.
MyProgram.java
package singleton.after;
public class MyProgram {
public static void main (String[] args) {
new FirstPage().setAndPrintSettings();
new SecondPage().printSettings();
}
}
Settings.java
🔍 getSettings
메소드를 잘 살펴보세요.
package singleton.after;
public class Settings {
private Settings () {};
private static Settings settings = null;
public static Settings getSettings () {
if (settings == null) {
settings = new Settings();
}
return settings;
}
private boolean darkMode = false;
private int fontSize = 13;
public boolean getDarkMode () { return darkMode; }
public int getFontSize () { return fontSize; }
public void setDarkMode (boolean _darkMode) {
darkMode = _darkMode; }
public void setFontSize (int _fontSize) {
fontSize = _fontSize; }
}
FirstPage.java
🔍 settings
변수에 어떻게 값이 들어가는지 주목하세요.
package singleton.after;
public class FirstPage {
private Settings settings = Settings.getSettings();
public void setAndPrintSettings () {
settings.setDarkMode(true);
settings.setFontSize(15);
System.out.println(settings.getDarkMode() + " " + settings.getFontSize());
}
}
SecondPage.java
package singleton.after;
public class SecondPage {
private Settings settings = Settings.getSettings();
public void printSettings () {
System.out.println(settings.getDarkMode() + " " + settings.getFontSize());
}
}
🌟 Mark Sunghun Park님께서 댓글로 보충해주신 내용입니다.
- 자바에서 라이브러리나 프레임웤없이 가장 안전하게 싱글톤을 생성할 수 있는 방법 중의 하나는 inner static class singleton입니다.
public class InnerStaticSingleton implements Serializable {
private InnerStaticSingleton() {
}
public static InnerStaticSingleton getInstance() {
return InnerClass.instance;
}
private static class InnerClass {
private static final InnerStaticSingleton instance = new InnerStaticSingleton();
}
}
- 이 방법을 사용하면 멀티쓰레드에 안전하고, Lazy loading을 지원하며, serialization에도 안전한 싱글톤을 만들 수 있습니다.
Strategy 패턴
Main.java
package strategy.after;
public class Main {
public static void main(String[] args) {
MyProgram myProgram = new MyProgram();
myProgram.testProgram();
}
}
MyProgram.java
package strategy.after;
public class MyProgram {
private SearchButton searchButton = new SearchButton(this);
public void setModeAll () {
searchButton.setSearchStrategy(new SearchStratagyAll());
}
public void setModeImage () {
searchButton.setSearchStrategy(new SearchStratagyImage());
}
public void setModeNews () {
searchButton.setSearchStrategy(new SearchStratagyNews());
}
public void setModeMap () {
searchButton.setSearchStrategy(new SearchStratagyMap());
}
public void testProgram () {
searchButton.onClick(); // "SEARCH ALL" 출력
setModeImage(); // 이미지검색 모드로
searchButton.onClick(); // "SEARCH IMAGE" 출력
setModeNews(); // 뉴스검색 모드로
searchButton.onClick(); // "SEARCH NEWS" 출력
setModeMap(); // 지도검색 모드로
searchButton.onClick(); // "SEARCH MAP" 출력
}
}
SearchStrategy.java
package strategy.after;
interface SearchStrategy {
public void search ();
}
class SearchStratagyAll implements SearchStrategy {
public void search () {
System.out.println("SEARCH ALL");
// 전체검색하는 코드
}
}
class SearchStratagyImage implements SearchStrategy {
public void search () {
System.out.println("SEARCH IMAGE");
// 이미지검색하는 코드
}
}
class SearchStratagyNews implements SearchStrategy {
public void search () {
System.out.println("SEARCH NEWS");
// 뉴스검색하는 코드
}
}
class SearchStratagyMap implements SearchStrategy {
public void search () {
System.out.println("SEARCH MAP");
// 지도검색하는 코드
}
}
SearchButton.java
package strategy.after;
public class SearchButton {
private MyProgram myProgram;
public SearchButton (MyProgram _myProgram) {
myProgram = _myProgram;
}
private SearchStrategy searchStrategy = new SearchStratagyAll();
public void setSearchStrategy (SearchStrategy _searchStrategy) {
searchStrategy = _searchStrategy;
}
public void onClick () {
searchStrategy.search();
}
}
State 패턴
MyProgram.java
package state;
public class MyProgram {
public static void main(final String[] args) {
final ModeSwitch modeSwitch = new ModeSwitch();
modeSwitch.onSwitch(); // "FROM LIGHT TO DARK" 출력
modeSwitch.onSwitch(); // "FROM DARK TO LIGHT" 출력
modeSwitch.onSwitch(); // "FROM LIGHT TO DARK" 출력
modeSwitch.onSwitch(); // "FROM DARK TO LIGHT" 출력
}
}
ModeState.java
package state;
public interface ModeState {
public void toggle (ModeSwitch modeSwitch);
}
class ModeStateLight implements ModeState {
public void toggle (ModeSwitch modeSwitch) {
System.out.println("FROM LIGHT TO DARK");
// 화면을 어둡게 하는 코드
// ..
// ..
modeSwitch.setState(new ModeStateDark());
}
}
class ModeStateDark implements ModeState {
public void toggle (ModeSwitch modeSwitch) {
System.out.println("FROM DARK TO LIGHT");
// 화면을 밝게 하는 코드
// ..
// ..
modeSwitch.setState(new ModeStateLight());
}
}
ModeStitch.java
package state;
public class ModeSwitch {
private ModeState modeState = new ModeStateLight();
public void setState (ModeState _modeState) {
modeState = _modeState;
}
public void onSwitch () {
modeState.toggle(this);
}
}
Command 패턴
MyProgram.java
package command;
public class MyProgram {
public static void main(String[] args) {
RobotKit robotKit = new RobotKit();
robotKit.addCommand(new MoveForwardCommand(2));
robotKit.addCommand(new TurnCommand(Robot.Direction.LEFT));
robotKit.addCommand(new MoveForwardCommand(1));
robotKit.addCommand(new TurnCommand(Robot.Direction.RIGHT));
robotKit.addCommand(new PickupCommand());
robotKit.start();
// 2 칸 전진
// 왼쪽으로 방향전환
// 1 칸 전진
// 오른쪽으로 방향전환
// 앞의 물건 집어들기
}
}
Robot.java
package command;
public class Robot {
public enum Direction { LEFT, RIGHT }
public void moveForward (int space) {
System.out.println(space + " 칸 전진");
}
public void turn (Direction _direction) {
System.out.println(
(_direction == Direction.LEFT ? "왼쪽" : "오른쪽") + "으로 방향전환"
);
}
public void pickup () {
System.out.println("앞의 물건 집어들기");
}
}
Command.java
package command;
abstract class Command {
protected Robot robot;
public void setRobot (Robot _robot) {
this.robot = _robot;
}
public abstract void execute ();
}
class MoveForwardCommand extends Command {
int space;
public MoveForwardCommand (int _space) {
space = _space;
}
public void execute () {
robot.moveForward(space);
}
}
class TurnCommand extends Command {
Robot.Direction direction;
public TurnCommand (Robot.Direction _direction) {
direction = _direction;
}
public void execute () {
robot.turn(direction);
}
}
class PickupCommand extends Command {
public void execute () {
robot.pickup();
}
}
RobotKit.java
package command;
import java.util.ArrayList;
public class RobotKit {
private Robot robot = new Robot();
private ArrayList<Command> commands = new ArrayList<Command>();
public void addCommand (Command command) {
commands.add(command);
}
public void start () {
for (Command command : commands) {
command.setRobot(robot);
command.execute();
}
}
}
Adapter 패턴
Strategy 패턴에 적용한 코드
FindAlgorithm.java
package adapter.search;
interface FindAlgorithm {
public void find (boolean global);
}
class FindMovieAlgorithm implements FindAlgorithm {
public void find (boolean global) {
System.out.println(
"find movie" + (global ? " globally" : "")
);
// 동영상검색하는 코드
// ...
// ...
}
}
SearchStrategy.java에 추가된 부분
//...
class SearchFindAdapter implements SearchStrategy {
private FindAlgorithm findAlgorithm;
public SearchFindAdapter (FindAlgorithm _findAlgorithm) {
findAlgorithm = _findAlgorithm;
}
public void search () {
findAlgorithm.find(true);
}
}
MyProgram.에 추가된 부분
//...
public void setModeMovie () {
searchButton.setSearchStrategy(
new SearchFindAdapter(new FindMovieAltorithm())
);
}
Command 패턴에 적용한 코드
Order.java
package adapter.robot;
interface Order {
public void run (Robot robot);
}
class MoveBackOrder implements Order {
private int block;
public MoveBackOrder(int _block) {
block = _block;
}
public void run (Robot robot) {
robot.turn(Robot.Direction.LEFT);
robot.turn(Robot.Direction.LEFT);
robot.moveForward(block);
}
}
Command.java에 추가된 부분
class CommandOrderAdapter extends Command {
private Order order;
public CommandOrderAdapter (Order _order) {
order = _order;
}
public void execute () {
order.run(robot);
}
}
MyProgram.java에 추가된 부분
class CommandOrderAdapter extends Command {
private Order order;
public CommandOrderAdapter (Order _order) {
order = _order;
}
public void execute () {
order.run(robot);
}
}
Proxy 패턴
MyProgram.java
package proxy;
import java.util.ArrayList;
public class MyProgram {
public static void main(String[] args) {
ArrayList<Thumbnail> thumbnails = new ArrayList<Thumbnail>();
thumbnails.add(new ProxyThumbnail("Git 강좌", "/git.mp4"));
thumbnails.add(new ProxyThumbnail("Rest API란?", "/rest-api.mp4"));
thumbnails.add(new ProxyThumbnail("도커 사용법", "/docker.mp4"));
thumbnails.add(new ProxyThumbnail("객체지향 프로그래밍", "/oodp.mp4"));
thumbnails.add(new ProxyThumbnail("블록체인의 원리", "/blockchain.mp4"));
for (Thumbnail thumbnail : thumbnails) {
thumbnail.showTitle();
}
// 제목:Git 강좌
// 제목:Rest API란?
// 제목:도커 사용법
// 제목:객체지향 프로그래밍
// 제목:블록체인의 원리
thumbnails.get(2).showPreview();
thumbnails.get(2).showPreview();
thumbnails.get(4).showPreview();
thumbnails.get(1).showPreview();
// /docker.mp4로부터 도커 사용법의 영상 데이터 다운
// 도커 사용법의 프리뷰 재생
// 도커 사용법의 프리뷰 재생
// /blockchain.mp4로부터 블록체인의 원리의 영상 데이터 다운
// 블록체인의 원리의 프리뷰 재생
// /rest-api.mp4로부터 Rest API란?의 영상 데이터 다운
// Rest API란?의 프리뷰 재생
}
}
Thumbnail.java
package proxy;
interface Thumbnail {
public void showTitle ();
public void showPreview ();
}
class RealThumbnail implements Thumbnail {
private String title;
private String movieUrl;
public RealThumbnail (String _title, String _movieUrl) {
title = _title;
movieUrl = _movieUrl;
// URL로부터 영상을 다운받는 작업 - 시간 소모
System.out.println(movieUrl + "로부터 " + title + "의 영상 데이터 다운");
}
public void showTitle () {
System.out.println("제목:" + title);
}
public void showPreview () {
System.out.println(title + "의 프리뷰 재생");
}
}
class ProxyThumbnail implements Thumbnail {
private String title;
private String movieUrl;
private RealThumbnail realThumbnail;
public ProxyThumbnail (String _title, String _movieUrl) {
title = _title;
movieUrl = _movieUrl;
}
public void showTitle () {
System.out.println("제목:" + title);
}
public void showPreview () {
if (realThumbnail == null) {
realThumbnail = new RealThumbnail(title, movieUrl);
}
realThumbnail.showPreview();
}
}
🍿 더 자세한 내용은 영상에서 보실 수 있습니다.
유튜브에서 영상 보기