組合模式(composite)
我們都知道檔案和檔案夾的概念,并且檔案是可以存放在檔案夾中,檔案夾中也可以存放其他檔案夾,需要設計一個簡單的程式來實作檔案夾和檔案的關系,
實作思路
- 檔案夾需要存放檔案夾和檔案,首先想到的是在檔案夾中設計倆個集合分別來存放檔案夾和檔案,
- 有展示檔案路徑需求時,不清楚在最里層是檔案夾還是檔案,所以需要把檔案夾和檔案當做一個物件來處理會更好,都是條目,所以需要創建一個他們共同的父類,
- 對檔案夾的設計優化,創建一個集合容器,容器型別是父類,即解決了既要存放檔案夾又要存放檔案的問題,
有時,將容器和內容作為同一種東西看待,可以很好的幫助我們處理問題,在容器中既可以存放內容,也可以存放小容器,然后在小容器中,又可以繼續放入更小的容器,這樣,這樣就行程了容器結構、遞回結構,
創造出這樣結構的模式就是組合模式(Composite),能夠是容器和記憶體具有一致性,創造出遞回結構的模式,
代碼實作
Composite:檔案和檔案夾的父類
public abstract class Component {
public Component(String name) {
this.name = name;
}
/**
* 名稱
*/
protected String name;
/**
* 展示方法
* @param prefix
*/
public abstract void show(String prefix);
/**
* 添加方法
* @param component
*/
public void add(Component component){
}
Folder:檔案夾類
public class Folder extends Component {
/**
* 存放composite的集合
*/
private List<Component> componentList = new ArrayList<>(16);
public Folder(String name) {
super(name);
}
@Override
public void show(String prefix) {
prefix += "/"+name;
String finalPrefix = prefix;
System.out.println(finalPrefix);
componentList.forEach(component -> component.show(finalPrefix));
}
@Override
public void add(Component component) {
componentList.add(component);
}
}
file:檔案類
public class File extends Component {
public File(String name) {
super(name);
}
@Override
public void show(String prefix) {
System.out.println(prefix+"/"+this.name);
}
}
main方法測驗
public static void main(String[] args) {
Folder folder = new Folder("根檔案夾");
Folder folder1 = new Folder("檔案夾1");
folder.add(folder1);
File file1 = new File("檔案1");
folder1.add(file1);
File file2 = new File("檔案2");
folder1.add(file2);
Folder folder2 = new Folder("檔案夾2");
folder.add(folder2);
File file3 = new File("檔案3");
folder2.add(file3);
File file4 = new File("檔案4");
folder2.add(file4);
File file5 = new File("檔案5");
folder2.add(file5);
File file6 = new File("檔案6");
folder.add(file6);
folder.show("");
System.out.println("----------------");
folder1.show("");
}
輸出結果:
類圖

訪問者模式
我們在對類中資料結構執行操作A時,一般會在該類中宣告一個方法來完成操作A,但是當需要增加另一種操作B時,就需要再增加一個方法,那么在后續不斷增加需求程序中,我們就需要不斷的去修改這個類,這樣就很不符合開閉原則,
那么我們能不能把資料結構和操作分開,當需要增加操作需求時,只需修改操作類呢?
訪問模式就可以實作這樣的需求,在該模式中,資料結構與處理被分離開來,撰寫一個表示“訪問者”的類來訪問資料
中的元素,并把對各元素的處理交給訪問者類,這樣,當需要增加新的處理時,我們只需要編 的訪問者,然后讓資料結構可以接受訪問者的訪問即可,
代碼實作
ELement:資料結構介面
public interface Element {
void accept(Visitor visitor);
}
Visitor:訪問者介面
public interface Visitor {
void visit(Element element);
}
ConcreteElement:具體資料結構實作類
public class ConcreteElement implements Element{
private String name;
private Integer age;
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "ConcreteElement{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
NameVisitor:修改名稱屬性的訪問者
public class NameVisitor implements Visitor {
@Override
public void visit(Element element) {
ConcreteElement concreteElement = (ConcreteElement) element;
concreteElement.setName("測驗");
}
}
AgeVisitor:修改年齡屬性的訪問者
public class AgeVisitor implements Visitor {
@Override
public void visit(Element element) {
ConcreteElement concreteElement = (ConcreteElement) element;
concreteElement.setAge(1);
}
}
main方法:
public static void main(String[] args) {
Element element = new ConcreteElement();
System.out.println(" 沒有被訪問結果輸出: "+element);
Visitor visitor = new NameVisitor();
element.accept(visitor);
System.out.println(" 被NameVisitor訪問結果輸出: "+element);
visitor = new AgeVisitor();
element.accept(visitor);
System.out.println(" 被AgeVisitor訪問結果輸出: "+element);
}
資料結果:
通過上面的代碼實作,可以看到ConcreteElement通過accept實作了對訪問者動態變更,通過傳入不同的訪問者實作類不同的操作需求,后期因需求的增加只需增加不同的訪問者,
類圖
倆個模式搭配干活
淺嘗
需求
在組合模式中,完成了一個檔案夾的設計,現在需要增加一個需求:對當前檔案夾中的檔案做名稱修改,
這個需求其實很簡單,首先想到應該就是遍歷檔案夾修改里面的檔案名稱,
那么有沒有更優雅的方式呢?試試訪問者模式
代碼實作
下面貼入的代碼已省略在組合模式已有的代碼,
檔案相關代碼:
public interface Element {
void accept(Visitor visitor);
}
public abstract class Component implements Element{}
public class File extends Component {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public class Folder extends Component {
@Override
public void accept(Visitor visitor) {
componentList.stream().forEach(component -> visitor.visit(component));
}
}
訪問者相關代碼:
public interface Visitor {
void visit(Element element);
}
public class UpdateFileNameVisitor implements Visitor {
@Override
public void visit(Element element) {
if(element instanceof Folder){
element.accept(this);
}else{
File file = (File) element;
file.setName("visitor update-"+file.getName());
}
}
}
main方法
public static void main(String[] args) {
// 省略已有代碼
folder.show("");
System.out.println("----------------");
Visitor visitor = new UpdateFileNameVisitor();
folder.accept(visitor);
folder.show("");
}
結果:
深入
在我們日常業務代碼中,經常會出現樹型結構資料,比商品分類,權限等,一般涉及到這樣的資料結構可以考慮使用組合模式+訪問模式來處理需求,
需求
在商品分類處理中
- 實作商品分類樹的查詢 ,
- 實作商品分類的洗掉,并洗掉他的子類,
代碼實作
商品分類資料結構
public class CommodityClass {
/**
* 標識
*/
private Long id;
/**
* 名稱
*/
private String name;
/**
* 父分類ID
*/
private Long parentId;
/**
* 商品分類子集
*/
private List<CommodityClass> Children;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<CommodityClass> getChildren() {
return Children;
}
public void setChildren(List<CommodityClass> children) {
Children = children;
}
public Long getParentId() {
return parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
public <T> T execute(CommodityClassOperation<T> commodityClassOperation){
return commodityClassOperation.doExecute(this);
}
@Override
public String toString() {
return "CommodityClass{" +
"id=" + id +
", name='" + name + '\'' +
", parentId=" + parentId +
", Children=" + Children +
'}';
}
}
service層處理類
public interface CommodityClassService {
/**
* 查詢商品分類結構樹
* @param commodityClassId 商品分類ID
* @return 商品分類結構樹
*/
CommodityClass getAllCommodityClassServiceById(Long commodityClassId);
/**
* 洗掉商品分類
* @param commodityClassId 商品分類ID
* @return true 成功 false 失敗
*/
Boolean deleteAllCommodityClassServiceById(Long commodityClassId);
}
@Service
public class CommodityClassServiceImpl implements CommodityClassService {
@Autowired
private QueryCommodityClassOperation queryCommodityClassOperation;
@Autowired
private DeleteCommodityClassOperation deleteCommodityClassOperation;
@Autowired
private CommodityClassDAO commodityClassDAO;
/**
* 查詢商品分類結構樹
* @param commodityClassId 商品分類ID
* @return 商品分類結構樹
*/
@Override
public CommodityClass getAllCommodityClassServiceById(Long commodityClassId){
CommodityClass commodityClassServiceById = commodityClassDAO.getCommodityClassServiceById(commodityClassId);
queryCommodityClassOperation.doExecute(commodityClassServiceById);
return commodityClassServiceById;
}
/**
* 洗掉商品分類
* @param commodityClassId 商品分類ID
* @return true 成功 false 失敗
*/
@Override
public Boolean deleteAllCommodityClassServiceById(Long commodityClassId){
CommodityClass commodityClassServiceById = commodityClassDAO.getCommodityClassServiceById(commodityClassId);
return deleteCommodityClassOperation.doExecute(commodityClassServiceById);
}
}
訪問者類
public interface CommodityClassOperation<T> {
T doExecute(CommodityClass commodityClass);
}
@Component
public class QueryCommodityClassOperation implements CommodityClassOperation<Boolean> {
@Autowired
private CommodityClassDAO commodityClassDAO;
@Override
public Boolean doExecute(CommodityClass commodityClass) {
List<CommodityClass> children =
commodityClassDAO.listCommodityClassServiceByParentId(commodityClass.getId());
if(!CollectionUtils.isEmpty(children)){
children.stream().forEach(commodityClass1 ->
commodityClass1.execute(this));
}
commodityClass.setChildren(children);
return true;
}
}
@Component
public class DeleteCommodityClassOperation implements CommodityClassOperation<Boolean> {
@Autowired
private CommodityClassDAO commodityClassDAO;
@Override
public Boolean doExecute(CommodityClass commodityClass) {
List<CommodityClass> children =
commodityClassDAO.listCommodityClassServiceByParentId(commodityClass.getId());
if(!CollectionUtils.isEmpty(children)){
children.stream().forEach(commodityClass1 ->
commodityClass1.execute(this));
}
commodityClassDAO.deletCommodityClassServiceById(commodityClass.getId());
return true;
}
}
DAO層查詢資料庫已省略可以自行實作,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/270945.html
標籤:設計模式
