Decorator 설계 패턴
1. Decorator 설계 패턴의 개념
- 객체에 동적으로 새로운 행동(기능)을 추가할 수 있는 패턴이다. 주로 상속 대신 사용되며, 원래 객체를 수정하지 않고도 행동을 확장할 수 있다.
- 구성요소
- 컴포넌트(Component) : 기본 인터페이스나 추상 클래스로, 구체적인 객체들과 데코레이터들이 구현할 공통 메서드를 정의합니다.
- 구체 컴포넌트(ConcreteComponent) : 실제로 기능을 구현하는 클래스입니다. 기본 컴포넌트의 구체적인 형태입니다.
- 데코레이터(Decorator) : 기본 컴포넌트의 인터페이스를 구현하면서, 추가된 행동을 제공하는 추상 클래스입니다. 이 클래스는 컴포넌트 객체를 래핑(wrapping)하여 동적으로 행동을 추가합니다.
- 구체 데코레이터(ConcreteDecorator): 데코레이터의 구체적인 구현으로, 추가된 행동을 정의합니다.
1) Decorator 사용 전
- 여러기능을 추가하기 위해서는 상속을 사용해서 추가하여야한다.
- 3가지 기능을 여러 조합으로 사용하면 무수히 많은 클래스가 나온다.
- 순서에 상관 없는 경우 8가지 클래스를 생성해야 하며 순서가 상관있는 경우 27가지의 클래스를 생성해야한다.
- 각클래스마다 생성자의 매개변수도 다양해서 일관성도 저해된다.
2) Decorator 사용 후
- 필요한 기능을 선택적으로 사용 가능하다.
- 인터페이스와 컴포넌트
public interface Printer {
void print(String s);
}
public class ContentPrinter implements Printer {
@Override
public void print(String s) {
System.out.println(s);
}
}
- 데코레이터와 구현데코레이터
코드 접기/펴기
public abstract class PrinterDecorator implements Printer {
protected Printer origin;
public PrinterDecorator(Printer printer) {
this.origin = printer;
}
}
public class HeaderPrinter extends PrinterDecorator {
String header;
public HeaderPrinter(Printer printer, String header) {
super(printer);
this.header = header;
}
@Override
public void print(String s) {
System.out.println(header);
origin.print(s);
}
}
public class FooterPrinter extends PrinterDecorator {
String footer;
public FooterPrinter(Printer printer, String footer) {
super(printer);
this.footer = footer;
}
@Override
public void print(String s) {
origin.print(s);
System.out.println(footer);
}
}
public class SignPrinter extends PrinterDecorator {
String sign;
public SignPrinter(Printer printer, String sign) {
super(printer);
this.sign = sign;
}
@Override
public void print(String s) {
origin.print(s);
System.out.println(" by " + sign);
}
}
- 예제
코드 접기/펴기
public class Test01 {
public static void main(String[] args) {
ContentPrinter printer = new ContentPrinter();
printer.print("안녕하세요");
System.out.println("---------printer 종료");
ContentPrinter printer2 = new ContentPrinter();
HeaderPrinter printer2H = new HeaderPrinter(printer2, "머릿말");
printer2H.print("안녕하세요");
System.out.println("---------printer2 종료");
ContentPrinter printer3 = new ContentPrinter();
HeaderPrinter printer3H = new HeaderPrinter(printer3, "머릿말");
FooterPrinter printer3F = new FooterPrinter(printer3H, "꼬릿말");
printer3F.print("안녕하세요");
System.out.println("---------printer3 종료");
ContentPrinter printer4 = new ContentPrinter();
FooterPrinter printer4F = new FooterPrinter(printer4, "꼬릿말");
printer4F.print("안녕하세요");
System.out.println("---------printer4 종료");
ContentPrinter printer5 = new ContentPrinter();
SignPrinter printer5S = new SignPrinter(printer5, "naknak");
printer5S.print("안녕하세요");
System.out.println("---------printer5 종료");
ContentPrinter printer6 = new ContentPrinter();
SignPrinter printer6S = new SignPrinter(printer5, "naknak");
HeaderPrinter printer6H = new HeaderPrinter(printer6S, "머릿말");
printer6H.print("안녕하세요");
System.out.println("---------printer6 종료");
}
}
- 결과
myApp에 적용하기
1. File I/O Stream
- File I/O Stream을 Dataa I/O Stream에 장착하여 Int,UTF,IEE-754를 읽어온다.
코드 접기/펴기
private void loadUser() {
try (FileInputStream in0 = new FileInputStream("user.data");
DataInputStream in = new DataInputStream(in0)) {
int userLength = in.readInt();
int maxUserNum = 0;
for (int i = 0; i < userLength; i++) {
User user = new User();
user.setNo(in.readInt());
user.setName(in.readUTF());
user.setEmail(in.readUTF());
user.setPassword(in.readUTF());
user.setTel(in.readUTF());
userList.add(user);
maxUserNum = Math.max(maxUserNum, user.getNo());
}
User.initSeqNo(maxUserNum);
} catch (IOException e) {
System.out.println("회원 정보 로딩 중 오류 발생" + e.getMessage());
}
}
private void saveUser() {
try (FileOutputStream out0 = new FileOutputStream("user.data");
DataOutputStream out = new DataOutputStream(out0)) {
out.writeInt(userList.size());
for (User user : userList) {
out.writeInt(user.getNo());
out.writeUTF(user.getName());
out.writeUTF(user.getEmail());
out.writeUTF(user.getPassword());
out.writeUTF(user.getTel());
}
} catch (IOException e) {
System.out.println("회원 정보 저장 중 오류 발생" + e.getMessage());
}
}
//user.java에 valueOf, getBytes는 삭제
2. 파일 직렬화/역직렬화(Object I/O Stream)
- 객체가 Serializble 인터페이스를 받아 구현체로 역할을 한다.
- 객체가 Serializble를 구현하면 Object I/O Stream을 통해 인스턴스된 객체를 byte[]로 변환 할 수 있다.
- Data I/O Stream처럼 데코레이터 패턴을 통해 File I/O Stream으로 내보낼 수 있다.
코드 접기/펴기
private void loadUser() {
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.data"))) {
userList = (List<User>) in.readObject();
int maxUserNum = 0;
for (User user : userList) {
maxUserNum = Math.max(maxUserNum, user.getNo());
}
User.initSeqNo(maxUserNum);
} catch (IOException | ClassNotFoundException e) {
System.out.println("회원 정보 로딩 중 오류 발생" + e.getMessage());
userList = new ArrayList<>();
}
}
private void saveUser() {
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.data"))) {
out.writeObject(userList);
} catch (IOException e) {
System.out.println("회원 정보 저장 중 오류 발생" + e.getMessage());
}
}
//User.java에서 implement를 통해 구현체로 만들기
public class User implements Serializable{/*생략*/}
3. File I/O API : csv포맷으로 출력하기
- 객체 정보를 ","로 구분한다.
코드 접기/펴기
private void loadUsers() {
try (Scanner in = new Scanner(new FileReader("user.csv"))) {
while (true) {
try {
String csv = in.nextLine();
userList.add(User.valueOf(csv));
} catch (Exception e) {
break;
}
}
int maxUserNo = 0;
for (User user : userList) {
if (user.getNo() > maxUserNo) {
maxUserNo = user.getNo();
}
}
User.initSeqNo(maxUserNo);
} catch (IOException e) {
System.out.println("회원 정보 로딩 중 오류 발생!");
// e.printStackTrace();
}
}
private void saveUsers() {
try (FileWriter out = new FileWriter("user.csv")) {
for (User user : userList) {
out.write(user.toCsvString() + "\n");
}
} catch (IOException e) {
System.out.println("회원 정보 저장 중 오류 발생!");
e.printStackTrace();
}
}
유저클래스 수정 접기/펴기
public static User valueOf(String csv) {
String[] values = csv.split(","); // csv: "1,홍길동,hong@test.com,1111,010-1111-2222"
User user = new User();
user.setNo(Integer.parseInt(values[0]));
user.setName(values[1]);
user.setEmail(values[2]);
user.setPassword(values[3]);
user.setTel(values[4]);
return user;
}
public String toCsvString() {
return new StringBuilder().append(no).append(",").append(name).append(",").append(email)
.append(",").append(password).append(",").append(tel).toString();
}
'개발자 꿈나무의 하루 > 01_Boot Camp' 카테고리의 다른 글
(네이버클라우드 부트캠프) 41일차 - Java프로그래밍 기초(파일 입출력) (0) | 2024.07.24 |
---|---|
(네이버클라우드 부트캠프) 40일차 - 실습프로젝트(Gson, Generic) (4) | 2024.07.23 |
(네이버클라우드 부트캠프) 38일차 - 실습프로젝트(파일입출력) (0) | 2024.07.19 |
(네이버클라우드 부트캠프) 37일차 - Java프로그래밍 기초(메서드 레퍼런스) (0) | 2024.07.18 |
(네이버클라우드 부트캠프) 36일차 - java프로그래밍 기초(람다식) (0) | 2024.07.18 |