* 행위패턴 (Behavioral Pattern) : 객체 간 기능 분배하는 일과 같은 로직 수행에 주로 이용하여 객체 간 연동에 대한 유형 제공
종류 | 개념 |
Template method | - 상위 클래스에서 처리 흐름 정하고 하위 클래스에서 구체적인 내용 재정의 |
Interpreter | - 간단한 언어 문법 정의하는 방법과 그 언어로 문장 구성하는 방법, 문장 해석하는 방법 제시 |
Iterator | - 집합 객체 요소들 내부 표현 방식 공개 않고 순차적으로 접근하는 구조 제공 |
Command | - 요청 자체를 객체화(캡슐화)하고 매개변수(파라미터) 추가하여 여러 가지 요구사항 추가 가능(로그기록, 작업 취소 지원) |
Chain of Resposibility | - 요청 처리 가능한 기회를 하나 이상 객체에 부여함으로써 객체 간 결합도 없애려 함 |
State | - 상태 일반적인 데이터 변수로 두지 않고 객체로 만들어 그 상태에 따른 행동들 분리 |
Strategy | - 상황에 따라 알고리즘 변경할 필요 있을 때, 각 알고리즘 클래스들을 공통된 인터페이스에 맞게 구현하여 다형성 활용 |
Mediator | - 중재자 통해 한 집합에 속해있는 객체들 상호작용 캡슐화 |
Memento | - 어떤 시점에서의 객체 상태 저장해 두었다가 필요 시 객체 그 시점 상태로 되돌림 |
Visitor | - 데이터 구조 안을 돌아다니는 주체인 '방문자'를 나타내는 클래스 준비해서 그 클래스에게 처리를 맡김으로서 기능 추가 시 유연성 제공 |
Observer | - 한 객체 상태가 바뀌면 그 객체에 의존하는 다른 객체들에게 통지되고 필요 시 자동으로 내용 갱신 |
* Template method Pattern
- 구현 별로 달라질 수 있는 행동(메서드)들은 구현 클래스에서 선언 후 호출하는 방식으로 사용
//Template method
//소스코드의 중복을 줄이기 위해 하위 클래스에서 빈번하고 공통적으로 나타나는 부분에 대해서 별도의 추상 클래스를 정의해서 사용
//상위 클래스(여기선 추상 클래스)에게 공통적인 로직은 템플릿 메서드를 두고,
//구체 클래스 스타일에 맞게 구현을 강제하기 위해 추상 메서드를 사용하고,
//Hook메서드(일반 메서드)를 두는 패턴
public abstract class HouseTemplate {
public final void buildHouse() {
buildFoundation();
buildPillars();
buildWalls();
buildWindows();
System.out.println("House is built");
}
private void buildWindows() {
System.out.println("Building Glass Windows");
}
public abstract void buildWalls();
public abstract void buildPillars();
private void buildFoundation(){
System.out.println("Building foundation with cement,iron rods and sand");
}
}
//concrete class
public class GlassHouse extends HouseTemplate {
@Override
public void buildWalls() {
System.out.println("Building Glass Walls");
}
@Override
public void buildPillars() {
System.out.println("Building Pillars with glass coating");
}
}
public class WoodenHouse extends HouseTemplate {
@Override
public void buildWalls() {
System.out.println("Building Wooden Walls");
}
@Override
public void buildPillars() {
System.out.println("Building Pillars with Wood coating");
}
}
public class HousingClient {
public static void main(String[] args) {
HouseTemplate houseType = new WoodenHouse();
houseType.buildHouse();
System.out.println();
System.out.println("***********");
System.out.println();
houseType = new GlassHouse();
houseType.buildHouse();
}
}
* Interpreter Pattern
- 일련의 규칙으로 정의된 언어를 해석
//Expression interface(혹은 abstract class)를 생성하고 Expression interface 구현하는 구상 클래스를 생성
//interpreterPatternDemo에서 규칙을 생성하고 main 함수 안에 정의된 언어 John male , Married Julie를 해석
public interface Expression {
public boolean interpret(String context);
}
public class AndExpression implements Expression {
private Expression expr1 = null;
private Expression expr2 = null;
public AndExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String context) {
return expr1.interpret(context) && expr2.interpret(context);
}
}
public class OrExpression implements Expression {
private Expression expr1 = null;
private Expression expr2 = null;
public OrExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String context) {
return expr1.interpret(context) || expr2.interpret(context);
}
}
public class TerminalExpression implements Expression {
private String data;
public TerminalExpression(String data){
this.data = data;
}
@Override
public boolean interpret(String context) {
if(context.contains(data)){
return true;
}
return false;
}
}
public class InterpreterPatternDemo {
//Rule: Robert or John are male
public static Expression getMaleExpression(){
Expression robert = new TerminalExpression("Robert");
Expression john = new TerminalExpression("John");
return new OrExpression(robert, john);
}
//Rule: Julie is a married women
public static Expression getMarriedWomanExpression(){
Expression julie = new TerminalExpression("Julie");
Expression married = new TerminalExpression("Married");
return new AndExpression(julie, married);
}
public static void main(String[] args) {
Expression isMale = getMaleExpression();
Expression isMarriedWoman = getMarriedWomanExpression();
System.out.println("John is male? " + isMale.interpret("John male"));
System.out.println("Julie is a married women? " + isMarriedWoman.interpret("Married Julie"));
}
}
* Iterator Pattern
- 서로 다른 구조를 가지고 있는 저장 객체에 대해서 접근하기 위해서 interface를 통일시키고 싶을 때 주로 사용
//goat는 자바에서 제공하는 hashMap의 Iterator를 (내부에 Iterator<E> 인터페이스를 구현)
//SheapIterator는 Iterator<E> 인터페이스를 구현하여 만든 Iterator
public interface Animal {
public void eat();
public void sleep();
public void sound();
}
public class Sheep implements Animal {
@Override
public void eat() {
System.out.println("길쭉한 풀 뜯어먹음");
}
@Override
public void sleep() {
System.out.println("앉아서 잠");
}
@Override
public void sound() {
System.out.println("메에에");
}
}
public class Goat implements Animal {
@Override
public void eat() {
System.out.println("짧은 풀 뜯어먹음");
}
@Override
public void sleep() {
System.out.println("엎드려서 잠");
}
@Override
public void sound() {
System.out.println("음메에에");
}
}
public class Goatherd {
public static final int MAX_GOATS = 100;
private int goatNum = 0;
public static final HashMap<Integer, Goat> GOATS = new HashMap<>();
public Goatherd() {
super();
int i;
for (i = 0; i < 30; ++i) {
GOATS.put(i, new Goat());
}
goatNum = i;
}
public void addGoat() {
if (GOATS.size() <= MAX_GOATS) GOATS.put(goatNum++, new Goat());
}
public void removeGoat() {
GOATS.remove(goatNum--);
}
public Iterator<Integer> createIterator() {
return GOATS.keySet().iterator();
}
}
public class SheepIterator implements Iterator<Sheep> {
private Sheep[] sheeps;
int position = 0;
public SheepIterator(Sheep[] sheeps) {
super();
this.sheeps = sheeps;
}
@Override
public boolean hasNext() {
if (position >= sheeps.length || sheeps[position] == null) {
return false;
} else {
return true;
}
}
@Override
public Sheep next() {
Sheep tempSheep = sheeps[position];
position++;
return tempSheep;
}
}
public class Client {
public static void main(String[] args) {
Goatherd goatherd = new Goatherd();
Shepherd shepherd = new Shepherd();
Iterator<Integer> goatIter = goatherd.createIterator();
Iterator<Sheep> sheepIter = shepherd.createIterator();
while(goatIter.hasNext()) {
Goat goat = goatherd.GOATS.get(goatIter.next());
goat.sound();
}
while(sheepIter.hasNext()) {
Sheep sheep = sheepIter.next();
sheep.sound();
}
}
}
* Command Pattern
- 이벤트가 발생했을 때 실행될 기능이 다양하면서도 변경이 필요한 경우에 이벤트를 발생시키는 클래스를 변경하지 않고 재사용하고자 할 때 유용
- 실행될 기능을 캡슐화함으로써 기능의 실행을 요구하는 호출자(Invoker) 클래스와 실제 기능을 실행하는 수신자(Receiver) 클래스 사이의 의존성을 제거
//실행될 기능을 캡슐화하고 , 실행될 기능을 커맨드를 통해 실행하는 패턴
//기능이 실행된 후에는 Receiver에 수행된 기능에 대하여 알려줌.
public interface Command { public abstract void execute(); }
//invoker
public class Button {
private Command theCommand;
public Button(Command theCommand) {
setCommand(theCommand);
}
public void setCommand(Command newCommand) {
this.theCommand = newCommand;
}
public void pressed() {
theCommand.execute();
}
}
//receiver
public class Alarm {
public void start() {
System.out.println("Alarming");
}
}
public class Lamp {
public void turnOn() {
System.out.println("Lamp On");
}
}
//concreteCommand
public class AlarmStartCommand implements Command {
private Alarm theAlarm;
public AlarmStartCommand(Alarm theAlarm) {
this.theAlarm = theAlarm;
}
public void execute() {
theAlarm.start();
}
}
public class LampOnCommand implements Command {
private Lamp theLamp;
public LampOnCommand(Lamp theLamp) {
this.theLamp = theLamp;
}
public void execute() {
theLamp.turnOn();
}
}
* Chain of Resposibility Pattern
- 요청 처리가 들어오게 되면 그것을 수신하는 객체가 자신이 처리할 수 없는 경우에는 다음 객체에게 문제를 넘김으로써 최종적으로 요청 처리 가능한 객체에 의해 처리 가능토록 함
- 각각의 처리 객체는 명령 객체를 처리할 수 있는 연산의 집합이고, 체인 안의 처리 객체가 핸들 할 수 없는 명령은 다음 처리 객체로 넘겨짐. 이 작동방식은 새로운 처리 객체부터 체인의 끝까지 다시 반복
- 예) try-catch-finally : try 안에서 Exception 발생하면 catch 로 이동하여 코드 수행
//들어온 Amount에 따라 Manager->Director -> VicePresident->President 순으로 Responsibliity를 check
//Check한후 자신의 ALLOWABLE보다 크다면 다음 PurchasePower으로 책임을 넘김
abstract class PurchasePower {
protected final double base = 500;
protected PurchasePower successor;
public void setSuccessor(PurchasePower successor) {
this.successor = successor;
}
abstract public void processRequest(PurchaseRequest request);
}
class ManagerPower extends PurchasePower {
private final double ALLOWABLE = 10 * base;
public void processRequest(PurchaseRequest request) {
if (request.getAmount() < ALLOWABLE) {
System.out.println("Manager will approve $" + request.getAmount());
} else if (successor != null) {
successor.processRequest(request);
}
}
}
class DirectorPower extends PurchasePower {
private final double ALLOWABLE = 20 * base;
public void processRequest(PurchaseRequest request) {
if (request.getAmount() < ALLOWABLE) {
System.out.println("Director will approve $" + request.getAmount());
} else if (successor != null) {
successor.processRequest(request);
}
}
}
class VicePresidentPower extends PurchasePower {
private final double ALLOWABLE = 40 * base;
public void processRequest(PurchaseRequest request) {
if (request.getAmount() < ALLOWABLE) {
System.out.println("Vice President will approve $" + request.getAmount());
} else if (successor != null) {
successor.processRequest(request);
}
}
}
class PresidentPower extends PurchasePower {
private final double ALLOWABLE = 60 * base;
public void processRequest(PurchaseRequest request) {
if (request.getAmount() < ALLOWABLE) {
System.out.println("President will approve $" + request.getAmount());
} else {
System.out.println( "Your request for $" + request.getAmount() + " needs a board meeting!");
}
}
}
class PurchaseRequest {
private int number;
private double amount;
private String purpose;
public PurchaseRequest(int number, double amount, String purpose) {
this.number = number;
this.amount = amount;
this.purpose = purpose;
}
public double getAmount() {
return amount;
}
public void setAmount(double amt) {
amount = amt;
}
public String getPurpose() {
return purpose;
}
public void setPurpose(String reason) {
purpose = reason;
}
public int getNumber(){
return number;
}
public void setNumber(int num) {
number = num;
}
}
class CheckAuthority {
public static void main(String[] args) {
ManagerPower manager = new ManagerPower();
DirectorPower director = new DirectorPower();
VicePresidentPower vp = new VicePresidentPower();
PresidentPower president = new PresidentPower();
manager.setSuccessor(director);
director.setSuccessor(vp);
vp.setSuccessor(president);
// Press Ctrl+C to end.
try {
while (true) {
System.out.println("Enter the amount to check who should approve your expenditure.");
System.out.print(">");
double d = Double.parseDouble(new BufferedReader(new InputStreamReader(System.in)).readLine());
manager.processRequest(new PurchaseRequest(0, d, "General"));
}
} catch(Exception e) {
System.exit(1);
}
}
}
* State Pattern
- 일련의 규칙에 따라 객체의 상태(State)를 변화시켜, 객체가 할 수 있는 행위 바꿈
- 상태에 따라 행동 변화하는 객체엔 모두 적용 가능
- 전략 패턴은 상속을 대체하려는 목적, 스테이트 패턴은 코드 내의 조건문들을 대체하려는 목적
public interface PowerState {
public void powerPush();
}
public class On implements PowerState{
public void powerPush(){
System.out.println("power on");
}
}
public class Off implements PowerState {
public void powerPush(){
System.out.println("power off");
}
}
public class Saving implements PowerState {
public void powerPush(){
System.out.println("power saving");
}
}
public class Laptop {
private PowerState powerState;
public Laptop(){
this.powerState = new Off();
}
public void setPowerState(PowerState powerState){
this.powerState = powerState;
}
public void powerPush(){
powerState.powerPush();
}
}
public class Client {
public static void main(String args[]){
Laptop laptop = new Laptop();
On on = new On();
Off off = new Off();
Saving saving = new Saving();
laptop.powerPush();
laptop.setPowerState(on);
laptop.powerPush();
laptop.setPowerState(saving);
laptop.powerPush();
laptop.setPowerState(off);
laptop.powerPush();
laptop.setPowerState(on);
laptop.powerPush();
}
}
* Strategy Pattern
- 객체의 행위를 동적으로 바꾸고 싶은 경우 직접 행위를 수정하지 않고 전략을 바꿔주기만 함으로써 행위를 유연하게 확장하는 방법
- 즉 , 객체가 할 수 있는 행위들을 각각의 전략으로 정의, 수정이 필요한 경우 전략을 바꾸는 것만으로도 수정이 가능하도록 만드는 패턴
//Strategy Pattern 미적용
public interface Movable {
public void move();
}
public class Train implements Movable{
public void move(){
System.out.println("선로를 통해 이동");
}
}
public class Bus implements Movable{
public void move(){
System.out.println("도로를 통해 이동");
}
}
public class Client {
public static void main(String args[]){
Movable train = new Train();
Movable bus = new Bus();
train.move();
bus.move();
}
}
//기차는 선로 따라 이동, 버스는 도로 따라 이동. 시간이 흘러 선로 따라 움직이는 버스가 개발되었다고 가정
public void move(){
System.out.println("선로를 따라 이동");
}
//위와 같이 수정하는 방식은 OCP에 위배
//위와 같은 문제 해결하기 위해 Strategy Pattern 사용 가능
//Strategy Pattern 사용
//전략 정의
public interface MovableStrategy {
public void move();
}
public class LoadStrategy implements MovableStrategy {
@Override
public void move() {
System.out.println("도로를 통해 이동");
}
}
public class RailLoadStrategy implements MovableStrategy {
@Override
public void move() {
System.out.println("선로를 통해 이동");
}
}
//운송수단 선언
public class Moving {
private MovableStrategy movableStrategy;
public void move() {
movableStrategy.move();
}
public void setMovableStrategy(MovableStrategy movableStrategy) {
this.movableStrategy = movableStrategy;
}
}
public class Bus extends Moving {}
public class Train extends Moving {}
//result
public class Client {
public static void main(String[] args) {
Moving train = new Train();
Moving bus = new Bus();
train.setMovableStrategy(new RailLoadStrategy());
bus.setMovableStrategy(new LoadStrategy());
train.move();
bus.move();
/** rail load bus development*/
bus.setMovableStrategy(new RailLoadStrategy());
bus.move();
}
}
* Mediator Pattern
- 클래스 간의 상호작용을 하나의 클래스에 위임하여 처리하는 패턴
- M:N의 관계에서 M:1의 관계로 복잡도를 떨어뜨려 유지보수 및 확장성에 유리
- 다른 객체의 존재를 모르는 상태에서도 메시지를 주고받을 수 있음
//ConcreteColleague클래스는 Colleague(동료) interface를 구현하고 있는 N개의 객체
//ConcreteCollegue 클래스 간 데이터 전달을 위하여 Mediator를 이용
//Mediator에 ConcreteColleage를 등록하고 연결다리로 사용
public class ChatUser {
private ChatMediator mediator;
private String name;
public ChatUser(ChatMediator mediator, String name) {
this.mediator = mediator;
this.name = name;
}
public void send(String msg) {
System.out.println(name + " Sending Message = " + msg);
mediator.sendMessage(msg, this);
}
public void receive(String msg) {
System.out.println(name + " Message received : " + msg);
}
}
public class ChatMediator {
private List<ChatUser> users = new ArrayList<>();
public void sendMessage(String msg, ChatUser user) {
users.stream()
.filter(u -> u != user)
.forEach(u -> u.receive(msg));
}
public ChatMediator addUser(ChatUser user) {
users.add(user);
return this;
}
}
public class MediatorTest {
public static void main(String[] args) {
ChatMediator mediator = new ChatMediator();
ChatUser john = new ChatUser(mediator,"John");
mediator.addUser(new ChatUser(mediator,"Alice"))
.addUser(new ChatUser(mediator,"Bob"))
.addUser(john);
john.send("Hi every one!");
}
}
//result
John: Sending Message= Hi everyone!
Alice: Message received: Hi everyone!
Bob: Message received: Hi everyone!
* Memento Pattern
- 3개의 액터 클래스를 사용. 메멘토에는 복원할 개체의 상태가 포함
- Originator : 현재 State를 가지고, Memento 객체와 Memento 객체 상태를 얻게 합니다.
- Memento : State를 가지고 있는 인스턴스입니다.
- CareTaker : Memento를 순서대로 저장합니다.
//Originator는 Memento 인스턴스를 생성하고 또다시 또 그 객체를 반환하는 역할.
//Craker는 Memento객체를 List에 순서대로 save
public class Memento {
private String state;
public Memento(String state){
this.state = state;
}
public String getState(){
return state;
}
}
public class Originator {
private String state;
public void setState(String state){
this.state = state;
}
public String getState(){
return state;
}
public Memento saveStateToMemento(){
return new Memento(state);
}
public void getStateFromMemento(Memento memento){
state = memento.getState();
}
}
public class CareTaker {
private List<Memento> mementoList = new ArrayList<Memento>();
public void add(Memento state){
mementoList.add(state);
}
public Memento get(int index){
return mementoList.get(index);
}
}
public class MementoPatternDemo {
public static void main(String[] args) {
Originator originator = new Originator();
CareTaker careTaker = new CareTaker();
originator.setState("State #1");
originator.setState("State #2");
careTaker.add(originator.saveStateToMemento());
originator.setState("State #3");
careTaker.add(originator.saveStateToMemento());
originator.setState("State #4");
System.out.println("Current State: " + originator.getState());
originator.getStateFromMemento(careTaker.get(0));
System.out.println("First saved State: " + originator.getState());
originator.getStateFromMemento(careTaker.get(1));
System.out.println("Second saved State: " + originator.getState());
}
}
//result
Current State: State #4
First saved State: State #2
Second saved State: State #3
* Visitor Pattern
- 데이터 구조와 연산을 분리하여 데이터 구조의 원소들을 변경하지 않고 새로운 연산을 추가할 수 있는 패턴. 새로운 연산을 추가하려면 새로운 방문자를 추가하기만 하면 됨
public interface Element {
public void accept(Visitor v);
}
public abstract class Entry implements Element {
String name;
public Entry(String name) {
this.name = name;
}
public abstract void add(Entry entry);
}
public class Directory extends Entry {
ArrayList<Entry> directory = new ArrayList();
public Directory(String name) {
super(name);
}
public void add(Entry entry) {
directory.add(entry);
}
public void accept(Visitor v) {
v.visit(this);
}
}
public class File extends Entry {
public File(String name) {
super(name);
}
public void add(Entry entry) {
}
public void accept(Visitor v) {
v.visit(this);
}
}
public abstract class Visitor {
public abstract void visit(File file);
public abstract void visit(Directory directory);
}
public class ViewVisitor extends Visitor {
private String Path = "";
public void visit(File file) {
System.out.println(Path + "/" + file.name);
}
public void visit(Directory dic) {
Path = Path + "/" + dic.name;
System.out.println(Path);
for (int i = 0; i < dic.directory.size(); i++) {
dic.directory.get(i).accept(this);
}
}
}
public class Client {
public static void main(String[] args){
Directory root = new Directory("root");
Directory bin = new Directory("bin");
Directory Lkt = new Directory("Lkt");
File file1 = new File("file1");
File file2 = new File("file2");
File file3 = new File("file3");
File file4 = new File("file4");
root.add(file1);
bin.add(file2);
bin.add(file3);
Lkt.add(file4);
root.add(Lkt);
root.add(bin);
root.accept(new ViewVisitor()); //경로 출력
}
}
//result
/root
/root/file1
/root/Lkt
/root/Lkt/file4
/root/Lkt/bin
/root/Lkt/bin/file2
/root/Lkt/bin/file3
* Observer Pattern
- 한 객체의 상태 변화에 따라 다른 객체의 상태도 연동되도록 일대다 객체 의존 관계를 구성하는 패턴
- PUSH : 주제 객체가 구독 객체에게 상태를 보내는 방식
- PULL : 구독 객체가 주제 객체에게서 상태를 가져가는(요청하는) 방식
- 데이터의 변경이 발생하였을 때 상대 클래스나 객체에 의존하지 않으면서 데이터 변경을 통보하고자 할 때 사용
//Subject클래스 상속하는 DashBoard에 Observer 등록하고 setMessage메서드 호출하면 subscriber에게 이벤트가 전달
public interface Subject {
public void add(Observer observer);
public void remove(Observer observer);
public void notifyObservers();
public void notifyObserver(Observer observer);
}
public class DashBoard implements Subject {
private List<Observer> displays;
private String content;
public DashBoard() {
System.out.println("DashBoard(Display Subject)Create");
displays = new LinkedList<Observer>();
}
@Override
public void add(Observer observer) {
displays.add(observer);
}
@Override
public void remove(Observer observer) {
if (displays.contains(observer)) {
displays.remove(observer);
}
}
@Override
public void notifyObservers() {
for (Observer display : displays) {
display.update(content);
}
}
//전달받은 특정 observer 에게만 update
@Override
public void notifyObserver(Observer observer) {
observer.update(content);
}
public void setMessage(String content) {
this.content = content;
notifyObservers();
}
}
public interface Observer {
public void update(String content);
}
//Subscriber 클래스에는 subject를 pull 하는 메서드를 호출
public class Subscriber implements Observer {
private String content;
private Subject subject;
public Subscriber(Subject subject) {
this.subject = subject;
subject.add(this);
}
public void pullContent() {
System.out.println("Pull Contents is");
subject.notifyObserver(this);
}
@Override
public void update(String content) {
this.content = content;
display();
}
private void display() {
System.out.println("subscriber 1");
System.out.println("content : " + content);
System.out.println("\n");
}
}
public class Subscriber2 implements Observer {
Subject subject;
private String content;
public Subscriber2(Subject subject) {
subject.add(this);
}
@Override
public void update(String content) {
this.content = content;
display();
}
private void display() {
System.out.println("subscriber 2");
System.out.println("content : " + content);
System.out.println("\n");
}
}
public class Messenger {
public static void main(String[] args) {
DashBoard dashBoard = new DashBoard(); //Subject class create
Subscriber subscriber = new Subscriber(dashBoard);
Subscriber2 subscriber2 = new Subscriber2(dashBoard);
dashBoard.setMessage("some event"); //Subject new message send
subscriber.pullContent();
subscriber2.pullContent();
}
}
//result
DashBoard(Display Subject)Create
subscriber 1
content : some event
subscriber 2
content : some event
Pull Contents is
subscriber 1
content : some event
Pull Contents is
subscriber 2
content : some event
'정보처리기사 > 필기' 카테고리의 다른 글
[정보처리기사 필기 요약] 클래스 설계 - 협약에 의한 설계 (Design by Contract) (0) | 2021.03.02 |
---|---|
[정보처리기사 필기 요약] 객체 지향 분석 방법론 (0) | 2021.03.02 |
[정보처리기사 필기 요약] 디자인 패턴(2) - 구조패턴 (0) | 2021.03.02 |
[정보처리기사 필기 요약] 디자인 패턴(1) - 생성패턴 (0) | 2021.03.02 |
[정보처리기사 필기 요약] 객체지향 설계원칙 (SOLID) (0) | 2021.03.02 |
댓글