Composite패턴은 우리가 많이 사용하는 컴퓨터의 파일관리자처럼 객체들을 트리 형태로 구조화 해서 데이터를 처리하는 패턴이다.
객체들을 트리 형태로 구조화 하여 그릇객체와 내용물객체를 동일하게 취급하기 위한 패턴
public abstract class Entry {
private Entry parent;
// 부모를 설정한다
protected void setParent(Entry parent) {
this.parent = parent;
}
// 이름을 가져온다
public abstract String getName();
// 크기를 가져온다
public abstract int getSize();
// 목록을 표시한다
public void printList() {
printList("");
}
// prefix를 앞에 붙여 목록을 표시한다
protected abstract void printList(String prefix);
// 문자열 표시
@Override
public String toString() {
return getName() + " (" + getSize() + ")";
}
// 전체 경로를 가져온다
public String getFullName() {
StringBuilder fullname = new StringBuilder();
Entry entry = this;
do {
fullname.insert(0, entry.getName());
fullname.insert(0, "/");
entry = entry.parent;
} while (entry != null);
return fullname.toString();
}
}
import java.util.ArrayList;
import java.util.List;
public class Directory extends Entry {
private String name;
private List<Entry> directory = new ArrayList<>();
public Directory(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public int getSize() {
int size = 0;
for (Entry entry: directory) {
size += entry.getSize();
}
return size;
}
@Override
protected void printList(String prefix) {
System.out.println(prefix + "/" + this);
for (Entry entry: directory) {
entry.printList(prefix + "/" + name);
}
}
public Entry add(Entry entry) {
directory.add(entry);
entry.setParent(this);
return this;
}
}
public class File extends Entry {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
@Override
public String getName() {
return name;
}
@Override
public int getSize() {
return size;
}
@Override
protected void printList(String prefix) {
System.out.println(prefix + "/" + this);
}
}
public class Main {
public static void main(String[] args) {
Directory rootdir = new Directory("root");
Directory usrdir = new Directory("usr");
rootdir.add(usrdir);
Directory youngjin = new Directory("youngjin");
usrdir.add(youngjin);
File file = new File("Composite.java", 100);
youngjin.add(file);
rootdir.printList();
System.out.println();
System.out.println("file = " + file.getFullName());
System.out.println("youngjin = " + youngjin.getFullName());
}
}
class Entry: CustomStringConvertible{
private var parent: Entry?
init(){}
func getName() -> String{return ""}
func getSize() -> Int{return 0}
func printList(_ prefix: String){}
func setParent(_ parent: Entry){
self.parent = parent
}
func printList(){
printList("")
}
//toString
var description: String {
return "\(getName()) (\(getSize()))"
}
func getFullName() -> String{
var tempStr: String = ""
var tmpEntry: Entry? = self
repeat {
if let e = tmpEntry {
tempStr = "/" + e.getName() + tempStr
tmpEntry = e.parent
}
} while tmpEntry != nil
return tempStr
}
}
CustomStringConvertible 프로토콜
class Directory: Entry{
private var name: String
private var directory = Array<Entry>()
init(_ name: String){
self.name = name
}
override func getName() -> String {
return name
}
override func getSize() -> Int {
var size: Int = 0
for i in directory{
size = size + i.getSize()
}
return size
}
override func printList(_ prefix: String){
print(prefix + "/" + self.name)
for entry in directory{
entry.printList(prefix + "/" + name)
}
}
func add(_ entry: Entry) -> Entry{
directory.append(entry)
entry.setParent(self)
return self
}
}
class File: Entry {
private var name: String
private var size: Int
init(_ name: String,_ size: Int) {
self.name = name
self.size = size
}
override func getName() -> String {
return name
}
override func getSize() -> Int {
return size
}
override func printList(_ prefix: String) {
print(prefix + "/" + self.name)
}
}
@main
struct Main {
static func main() {
let rootdir: Directory = Directory("root")
let usrdir: Directory = Directory("usr")
let youngjin: Directory = Directory("youngjin")
rootdir.add(usrdir)
usrdir.add(youngjin)
let file: File = File("Composite.swift", 100)
youngjin.add(file)
rootdir.printList()
print()
print("file = \(file.getFullName())")
print("youngjin = \(youngjin.getFullName())")
}
}
자바의 경우 추상클래스 였으나 swift로 구현하면서 프로토콜과 일반 클래스 사이에서 고민했는데 swift의 프로토콜의 경우 저장프로퍼티를 허용하지 않고 구현또한 extension이라는 확장기능을 이용해야 해서 그냥 클래스로 구현하였다.
오버 라이딩 되어야 되는 부분은 그냥 0 또는 공백문자를 반환하는 형태로 만들었다.
tree형태로 객체를 다루어야 하는 상황이 온다면 나름 유용한 데이터 형태가 될 수 있다고 생각된다.