《设计模式》12.组合模式(结构型)
【《设计模式》12.组合模式(结构型)】又称“整体—部分”模式,组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结构,使用户对单个对象和组合对象具有访问一致性。
角色 抽象构件(Component):抽象类,定义叶子构件和树枝构件的公共接口,可提供默认实现,叶子构件中提供空实现或抛出异常
叶子构件(Leaf):没有子节点,实现抽象构件中的公共接口(空实现或抛出异常)
树枝构件(Composite):包含子节点,子节点可以是叶子构件也可以是树枝构件,实现抽象构件中公共接口
抽象构件 Component
public abstract class Component {
protected static final String SPACE = " ";
protected static final String hyphen = "-";
private String name;
public Component(String name) {
this.name = name;
}public String getName() {
return this.name;
}public abstract boolean add(Component component);
public abstract Component getChild(int index);
public abstract boolean remove(Component c);
public abstract void operation(int spaces);
}
叶子构件 Leaf
import java.util.Collections;
public class Leaf extends Component {
public Leaf(String name) {
super(name);
}@Override
public boolean add(Component component) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}@Override
public Component getChild(int index) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}@Override
public boolean remove(Component c) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}@Override
public void operation(int spaces) {
StringBuffer line = new StringBuffer();
if (spaces > 0) {
line.append(String.join("", Collections.nCopies(spaces, SPACE)));
line.append("|- ");
}
line.append(getName());
System.out.println(line);
}
}
树枝构件 Composite
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Composite extends Component {
private List components = new ArrayList();
public Composite(String name) {
super(name);
}@Override
public boolean add(Component component) {
return components.add(component);
}@Override
public Component getChild(int index) {
return components.get(index);
}@Override
public boolean remove(Component c) {
return components.remove(c);
}@Override
public void operation(int spaces) {
StringBuffer line = new StringBuffer();
if (spaces > 0) {
line.append(String.join("", Collections.nCopies(spaces, SPACE)));
line.append("|- ");
}
line.append(getName());
System.out.println(line);
int length = line.length() - spaces;
for (Component component : components) {
int mid = (length % 2 == 0) ? (length / 2 - 1) : (length / 2 + 1);
component.operation(mid + spaces);
}
}
}
测试类
public class CompositeTester {
public static void main(String args[]) {
Component root = new Composite("root");
root.add(new Leaf("node#1"));
Component branch2 = new Composite("branch#2");
root.add(branch2);
root.add(new Leaf("node#2"));
branch2.add(new Leaf("node#3"));
branch2.add(new Leaf("node#4"));
Composite branch3 = new Composite("branch#3");
branch2.add(branch3);
branch3.add(new Leaf("node#5"));
branch3.add(new Leaf("node#6"));
branch3.add(new Leaf("node#7"));
root.operation(0);
Component leaf = new Leaf("node#8");
leaf.add(new Leaf("node#9"));
}
}
输出
root
|- node#1
|- branch#2
|- node#3
|- node#4
|- branch#3
|- node#5
|- node#6
|- node#7
|- node#2
Exception in thread "main" java.lang.UnsupportedOperationException: Leaf
at com.designpatterns.composite.base.Leaf.add(Leaf.java:12)
at com.designpatterns.composite.base.CompositeTester.main(CompositeTester.java:23)
举例:文件 & 文件夹 抽象构件 File
import lombok.Getter;
import lombok.Setter;
import java.util.List;
public abstract class File {
@Getter @Setter
private String pathname;
@Getter @Setter
private String path;
@Getter @Setter
private String fileName;
public File(String pathname) {
if (pathname == null) {
throw new IllegalArgumentException(pathname);
}
pathname = pathname.replace("/", java.io.File.separator);
if (!pathname.contains(java.io.File.separator)) {
throw new IllegalArgumentException(pathname);
}
this.pathname = pathname;
int indexOfSep = pathname.lastIndexOf(java.io.File.separator);
this.path = pathname.substring(0, indexOfSep);
this.fileName = pathname.substring(indexOfSep + 1);
}public File(File parent, String fileName) {
if (parent == null) {
throw new IllegalArgumentException("null");
}
if (fileName == null || fileName.isEmpty()) {
throw new IllegalArgumentException(fileName);
}this.path = parent.getPathname();
this.fileName = fileName;
this.pathname = parent.getPathname() + java.io.File.separator + fileName;
}public static boolean isDirectory(String fileName) {
return !fileName.contains(".");
}public abstract boolean add(File f);
public abstract File getChild(String fileName);
public abstract boolean remove(String fileName);
public abstract List> list();
}
叶子构件 Text
import java.util.List;
public class Text extends File {
public Text(String pathname) {
super(pathname);
}public Text(File parent, String fileName) {
super(parent, fileName);
}@Override
public boolean add(File f) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}@Override
public File getChild(String fileName) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}@Override
public boolean remove(String fileName) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}@Override
public List> list() {
throw new UnsupportedOperationException(getClass().getSimpleName());
}
}
叶子构件 Audio
import java.util.List;
public class Audio extends File {
public Audio(String pathname) {
super(pathname);
}public Audio(File parent, String fileName) {
super(parent, fileName);
}@Override
public boolean add(File f) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}@Override
public File getChild(String fileName) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}@Override
public boolean remove(String fileName) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}@Override
public List> list() {
throw new UnsupportedOperationException(getClass().getSimpleName());
}
}
叶子构件 Video
import java.util.List;
public class Video extends File {
public Video(String pathname) {
super(pathname);
}public Video(File parent, String fileName) {
super(parent, fileName);
}@Override
public boolean add(File f) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}@Override
public File getChild(String fileName) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}@Override
public boolean remove(String fileName) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}@Override
public List> list() {
throw new UnsupportedOperationException(getClass().getSimpleName());
}
}
树枝构件 Directory
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Directory extends File {
private List files = new ArrayList<>();
public Directory(String pathname) {
super(pathname);
}public Directory(File parent, String fileName) {
super(parent, fileName);
}@Override
public boolean add(File f) {
return files.add(f);
}@Override
public File getChild(String fileName) {
Iterator iter = files.iterator();
while (iter.hasNext()) {
File f = iter.next();
if (f.getFileName().equals(fileName)) {
return f;
}
}
return null;
}@Override
public boolean remove(String fileName) {
Iterator iter = files.iterator();
while (iter.hasNext()) {
File f = iter.next();
if (f.getFileName().equals(fileName)) {
iter.remove();
return true;
}
}
return false;
}@Override
public List> list() {
List> pathnames = new ArrayList<>();
for (File f : files) {
pathnames.add(f.getPathname());
if (isDirectory(f.getFileName())) {
pathnames.addAll(f.list());
}
}
return pathnames;
}
}
测试类
import java.util.List;
public class FileTester {
public static void main(String args[]) {
File root = new Directory("D:/root");
File readme = new Text(root, "readme.txt");
root.add(readme);
File musicListTxt = new Text(root, "music-list.txt");
root.add(musicListTxt);
File musicDir = new Directory(root, "musics");
File giveMeYourHeart = new Audio(musicDir, "give you my heart.mp3");
File heyJude = new Audio(musicDir, "hey judy.wma");
musicDir.add(giveMeYourHeart);
musicDir.add(heyJude);
root.add(musicDir);
File videoListTxt = new Text(root, "video-list.txt");
root.add(videoListTxt);
File videoDir = new Directory(root, "videos");
File superMan = new Video(videoDir, "超人.rmvb");
videoDir.add(superMan);
root.add(videoDir);
List> pathnames = root.list();
for (String pathname : pathnames) {
System.out.println(pathname);
}File file = root.getChild("readme.txt");
System.out.println(file.getPathname());
}
}
D:\root\readme.txt
D:\root\music-list.txt
D:\root\musics
D:\root\musics\give you my heart.mp3
D:\root\musics\hey judy.wma
D:\root\video-list.txt
D:\root\videos
D:\root\videos\超人.rmvb
D:\root\readme.txt
举例2:html元素
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.util.HashMap;
import java.util.Map;
public abstract class Element {
@Getter
private String tagName;
protected long height;
protected long lineHeight;
protected long width;
protected Margin margin = new Margin();
private Border border = new Border();
protected Padding padding = new Padding();
protected Map, Object> styleCache = new HashMap<>();
@Getter @Setter
private String id;
public Element(String tagName) {
this.tagName = tagName;
}public Element(String tagName, String id) {
this.tagName = tagName;
this.id = id;
}public abstract boolean add(Element e);
public abstract Element getChild(String id);
public abstract boolean remove(Element e);
public abstract boolean empty();
public abstract void print(int tabs);
public void addStyle(String name, Object value) {
this.styleCache.put(name, value);
}public void removeStyle(String name) {
this.styleCache.remove(name);
}public void setBorderTop(long top) {
this.border.setTop(top);
this.styleCache.put("border-top", top);
}public void setBorderRight(long right) {
this.border.setRight(right);
this.styleCache.put("border-right", right);
}public void setBorderBottom(long bottom) {
this.border.setBottom(bottom);
this.styleCache.put("border-bottom", bottom);
}public void setBorderLeft(long left) {
this.border.setLeft(left);
this.styleCache.put("border-left", left);
}public void setBorder(long topAndBottom, long leftAndRight) {
this.border.setTop(topAndBottom);
this.border.setRight(leftAndRight);
this.border.setBottom(topAndBottom);
this.border.setLeft(leftAndRight);
this.styleCache.put("border-top", topAndBottom);
this.styleCache.put("border-right", leftAndRight);
this.styleCache.put("border-bottom", topAndBottom);
this.styleCache.put("border-left", leftAndRight);
}public void setBorder(long top, long right, long bottom, long left) {
this.border.setTop(top);
this.border.setRight(right);
this.border.setBottom(bottom);
this.border.setLeft(left);
this.styleCache.put("border-top", top);
this.styleCache.put("border-right", right);
this.styleCache.put("border-bottom", bottom);
this.styleCache.put("border-left", left);
}public void setMarginTop(long top) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}public void setMarginRight(long right) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}public void setMarginBottom(long bottom) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}public void setMarginLeft(long left) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}public void setMargin(long topAndBottom, long leftAndRight) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}public void setMargin(long top, long right, long bottom, long left) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}public void setPaddingTop(long top) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}public void setPaddingRight(long right) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}public void setPaddingBottom(long bottom) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}public void setPaddingLeft(long left) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}public void setPadding(long topAndBottom, long leftAndRight) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}public void setPadding(long top, long right, long bottom, long left) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}@Data
public class Margin {
private long top;
private long left;
private long right;
private long bottom;
}@Data
public class Border {
private long top;
private long left;
private long right;
private long bottom;
}@Data
public class Padding {
private long top;
private long left;
private long right;
private long bottom;
}
}
import java.util.Collections;
import java.util.Map;
public class InlineElement extends Element {
private String text;
public InlineElement(String tagName) {
super(tagName);
addStyle("display", "inline");
}public InlineElement(String tagName, String id) {
super(tagName, id);
addStyle("display", "inline");
}public String getText() {
return text;
}public void setText(String text) {
this.text = text;
}@Override
public boolean add(Element e) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}@Override
public Element getChild(String id) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}@Override
public boolean remove(Element e) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}@Override
public boolean empty() {
text = "";
return true;
}@Override
public void print(int tabs) {
String prefix = String.join("", Collections.nCopies(tabs, "\t"));
StringBuffer styles = new StringBuffer();
for (Map.Entry, Object> style : styleCache.entrySet()) {
if (styles.length() > 0) {
styles.append(";
");
}
styles.append(style.getKey()).append(":").append(style.getValue());
}
StringBuffer content = new StringBuffer();
content.append(prefix).append("<").append(getTagName());
if (getId() != null && !getId().isEmpty()) {
content.append(" id=\"").append(getId());
}
content.append("\" style=\"").append(styles).append("\">");
if (text != null && !text.isEmpty()) {
content.append(text);
}
content.append("").append(getTagName()).append(">");
System.out.println(content);
}
}
import java.util.Collections;
import java.util.Map;
public class InlineBlockElement extends Element {
private String text;
public InlineBlockElement(String tagName) {
super(tagName);
addStyle("display", "inline-block");
addStyle("float", "left");
// or right
addStyle("position", "absolute");
// or fixed
}public InlineBlockElement(String tagName, String id) {
super(tagName, id);
addStyle("display", "inline-block");
addStyle("float", "left");
// or right
addStyle("position", "absolute");
// or fixed
}public String getText() {
return text;
}public void setText(String text) {
this.text = text;
}@Override
public boolean add(Element e) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}@Override
public Element getChild(String id) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}@Override
public boolean remove(Element e) {
throw new UnsupportedOperationException(getClass().getSimpleName());
}@Override
public boolean empty() {
text = "";
return true;
}@Override
public void print(int tabs) {
String prefix = String.join("", Collections.nCopies(tabs, "\t"));
StringBuffer styles = new StringBuffer();
for (Map.Entry, Object> style : styleCache.entrySet()) {
if (styles.length() > 0) {
styles.append(";
");
}
styles.append(style.getKey()).append(":").append(style.getValue());
}
StringBuffer content = new StringBuffer();
content.append(prefix).append("<").append(getTagName());
if (getId() != null && !getId().isEmpty()) {
content.append(" id=\"").append(getId());
}
content.append("\" style=\"").append(styles).append("\">");
if (text != null && !text.isEmpty()) {
content.append(text);
}
content.append(prefix).append("").append(getTagName()).append(">");
System.out.println(content);
}public void setMarginTop(long top) {
this.margin.setTop(top);
this.styleCache.put("margin-top", top);
}public void setMarginRight(long right) {
this.margin.setRight(right);
this.styleCache.put("margin-right", right);
}public void setMarginBottom(long bottom) {
this.margin.setBottom(bottom);
this.styleCache.put("margin-bottom", bottom);
}public void setMarginLeft(long left) {
this.margin.setLeft(left);
this.styleCache.put("margin-left", left);
}public void setMargin(long topAndBottom, long leftAndRight) {
this.margin.setTop(topAndBottom);
this.margin.setRight(leftAndRight);
this.margin.setBottom(topAndBottom);
this.margin.setLeft(leftAndRight);
this.styleCache.put("margin-top", topAndBottom);
this.styleCache.put("margin-right", leftAndRight);
this.styleCache.put("margin-bottom", topAndBottom);
this.styleCache.put("margin-left", leftAndRight);
}public void setMargin(long top, long right, long bottom, long left) {
this.margin.setTop(top);
this.margin.setRight(right);
this.margin.setBottom(bottom);
this.margin.setLeft(left);
this.styleCache.put("margin-top", top);
this.styleCache.put("margin-right", right);
this.styleCache.put("margin-bottom", bottom);
this.styleCache.put("margin-left", right);
}public void setPaddingTop(long top) {
this.padding.setTop(top);
this.styleCache.put("padding-top", top);
}public void setPaddingRight(long right) {
this.padding.setRight(right);
this.styleCache.put("padding-right", right);
}public void setPaddingBottom(long bottom) {
this.padding.setBottom(bottom);
this.styleCache.put("padding-bottom", bottom);
}public void setPaddingLeft(long left) {
this.padding.setLeft(left);
this.styleCache.put("padding-left", left);
}public void setPadding(long topAndBottom, long leftAndRight) {
this.padding.setTop(topAndBottom);
this.padding.setRight(leftAndRight);
this.padding.setBottom(topAndBottom);
this.padding.setLeft(leftAndRight);
this.styleCache.put("padding-top", topAndBottom);
this.styleCache.put("padding-right", leftAndRight);
this.styleCache.put("padding-bottom", topAndBottom);
this.styleCache.put("padding-left", leftAndRight);
}public void setPadding(long top, long right, long bottom, long left) {
this.padding.setTop(top);
this.padding.setRight(right);
this.padding.setBottom(bottom);
this.padding.setLeft(left);
this.styleCache.put("padding-top", top);
this.styleCache.put("padding-right", right);
this.styleCache.put("padding-bottom", bottom);
this.styleCache.put("padding-left", left);
}
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class BlockElement extends Element {
private String text;
private Element parent;
private List children = new ArrayList<>();
public BlockElement(String tagName) {
super(tagName);
addStyle("display", "block");
}public BlockElement(String tagName, String id) {
super(tagName, id);
addStyle("display", "block");
}public String getText() {
return text;
}public void setText(String text) {
this.text = text;
}public long getHeight() {
return this.height;
}public void setHeight(long height) {
this.height = height;
this.styleCache.put("height", height);
}public long getLineHeight() {
return this.lineHeight;
}public void setLineHeight(long lineHeight) {
this.lineHeight = lineHeight;
this.styleCache.put("line-height", lineHeight);
}public long getWidth() {
return this.width;
}public void setWidth(long width) {
this.width = width;
this.styleCache.put("width", width);
}@Override
public boolean add(Element e) {
return children.add(e);
}@Override
public Element getChild(String id) {
for (Element child : children) {
if (child.getId().equals(id)) {
return child;
}
}
return null;
}@Override
public boolean remove(Element e) {
return children.remove(e);
}@Override
public boolean empty() {
children.clear();
return true;
}@Override
public void print(int tabs) {
StringBuffer styles = new StringBuffer();
for (Map.Entry, Object> style : styleCache.entrySet()) {
if (styles.length() > 0) {
styles.append(";
");
}
styles.append(style.getKey()).append(":").append(style.getValue());
}
String prefix = String.join("", Collections.nCopies(tabs, "\t"));
StringBuffer startTag = new StringBuffer();
startTag.append(prefix).append("<").append(getTagName());
if (getId() != null && !getId().isEmpty()) {
startTag.append(" id=\"").append(getId());
}
startTag.append("\" style=\"").append(styles).append("\">" );
System.out.println(startTag);
for (Element child : children) {
child.print(tabs + 1);
}
System.out.println(prefix + "" + getTagName() + ">");
}public void setMarginTop(long top) {
this.margin.setTop(top);
this.styleCache.put("margin-top", top);
}public void setMarginRight(long right) {
this.margin.setRight(right);
this.styleCache.put("margin-right", right);
}public void setMarginBottom(long bottom) {
this.margin.setBottom(bottom);
this.styleCache.put("margin-bottom", bottom);
}public void setMarginLeft(long left) {
this.margin.setLeft(left);
this.styleCache.put("margin-left", left);
}public void setMargin(long topAndBottom, long leftAndRight) {
this.margin.setTop(topAndBottom);
this.margin.setRight(leftAndRight);
this.margin.setBottom(topAndBottom);
this.margin.setLeft(leftAndRight);
this.styleCache.put("margin-top", topAndBottom);
this.styleCache.put("margin-right", leftAndRight);
this.styleCache.put("margin-bottom", topAndBottom);
this.styleCache.put("margin-left", leftAndRight);
}public void setMargin(long top, long right, long bottom, long left) {
this.margin.setTop(top);
this.margin.setRight(right);
this.margin.setBottom(bottom);
this.margin.setLeft(left);
this.styleCache.put("margin-top", top);
this.styleCache.put("margin-right", right);
this.styleCache.put("margin-bottom", bottom);
this.styleCache.put("margin-left", right);
}public void setPaddingTop(long top) {
this.padding.setTop(top);
this.styleCache.put("padding-top", top);
}public void setPaddingRight(long right) {
this.padding.setRight(right);
this.styleCache.put("padding-right", right);
}public void setPaddingBottom(long bottom) {
this.padding.setBottom(bottom);
this.styleCache.put("padding-bottom", bottom);
}public void setPaddingLeft(long left) {
this.padding.setLeft(left);
this.styleCache.put("padding-left", left);
}public void setPadding(long topAndBottom, long leftAndRight) {
this.padding.setTop(topAndBottom);
this.padding.setRight(leftAndRight);
this.padding.setBottom(topAndBottom);
this.padding.setLeft(leftAndRight);
this.styleCache.put("padding-top", topAndBottom);
this.styleCache.put("padding-right", leftAndRight);
this.styleCache.put("padding-bottom", topAndBottom);
this.styleCache.put("padding-left", leftAndRight);
}public void setPadding(long top, long right, long bottom, long left) {
this.padding.setTop(top);
this.padding.setRight(right);
this.padding.setBottom(bottom);
this.padding.setLeft(left);
this.styleCache.put("padding-top", top);
this.styleCache.put("padding-right", right);
this.styleCache.put("padding-bottom", bottom);
this.styleCache.put("padding-left", left);
}
}
public class ElementTester {
public static void main(String args[]) {
Element div = new BlockElement("div", "content");
InlineElement span = new InlineElement("span", "title");
span.setText("设计模式之组合模式");
Element a = new InlineElement("a", "goBaidu");
Element input = new InlineBlockElement("input", "clickMe");
Element content = new BlockElement("div", "description");
Element ul = new BlockElement("ul", "ul");
BlockElement li1 = new BlockElement("li", "li-1");
li1.setText("1. 通过 a 标签跳到百度首页");
BlockElement li2 = new BlockElement("li", "li-2");
li1.setText("2. 这是一个点击按钮");
ul.add(li1);
ul.add(li2);
content.add(ul);
div.add(span);
div.add(a);
div.add(input);
div.add(content);
div.print(0);
}
}
设计模式之组合模式
-
-
推荐阅读
- 慢慢的美丽
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量
- 《跨界歌手》:亲情永远比爱情更有泪点
- 诗歌:|诗歌: 《让我们举起世界杯,干了!》
- 期刊|期刊 | 国内核心期刊之(北大核心)
- 《魔法科高中的劣等生》第26卷(Invasion篇)发售
- 人间词话的智慧
- 《一代诗人》37期,生活,江南j,拨动心潭的一泓秋水
- 广角叙述|广角叙述 展众生群像——试析鲁迅《示众》的展示艺术
- 书评——《小行星》