领略设计模式的魅力,谈谈组合模式搭配访问者模式

简介: 组合模式(composite)我们都知道文件和文件夹的概念,并且文件是可以存放在文件夹中,文件夹中也可以存放其他文件夹。需要设计一个简单的程序来实现文件夹和文件的关系。实现思路文件夹需要存放文件夹和文件,首先想到的是在文件夹中设计两个集合分别来存放文件夹和文件。有展示文件路径需求时,不清楚在最里层是文件夹还是文件,所以需要把文件夹和文件当做一个对象来处理会更好,都是条目。所以需要创建一个他们共同的父类。对文件夹的设计优化,创建一个集合容器,容器类型是父类,即解决了既要存放文件夹又要存放文件的问题。

组合模式(composite)

我们都知道文件和文件夹的概念,并且文件是可以存放在文件夹中,文件夹中也可以存放其他文件夹。需要设计一个简单的程序来实现文件夹和文件的关系。

实现思路

  1. 文件夹需要存放文件夹和文件,首先想到的是在文件夹中设计两个集合分别来存放文件夹和文件。
  2. 有展示文件路径需求时,不清楚在最里层是文件夹还是文件,所以需要把文件夹和文件当做一个对象来处理会更好,都是条目。所以需要创建一个他们共同的父类。
  3. 对文件夹的设计优化,创建一个集合容器,容器类型是父类,即解决了既要存放文件夹又要存放文件的问题。

有时,将容器和内容作为同一种东西看待,可以很好地帮助我们处理问题,在容器中既可以存放内容,也可以存放小容器,然后在小容器中,又可以继续放入更小的容器。这样,这样就形成了容器结构、递归结构。

创造出这样结构的模式就是组合模式(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("");
    }

结果:

网络异常,图片无法展示
|

深入

在我们日常业务代码中,经常会出现树型结构数据。比商品分类,权限等,一般涉及到这样的数据结构可以考虑使用组合模式+访问模式来处理需求。

需求

在商品分类处理中

  1. 实现商品分类树的查询 。
  2. 实现商品分类的删除,并删除他的子类。

代码实现

商品分类数据结构

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层查询数据库已省略可以自行实现。

相关文章
|
5月前
|
设计模式 JavaScript 前端开发
js设计模式【详解】—— 组合模式
js设计模式【详解】—— 组合模式
64 7
|
2月前
|
设计模式 JavaScript 前端开发
JavaScript设计模式--访问者模式
【10月更文挑战第1天】
38 3
|
3月前
|
设计模式 Java
Java设计模式:组合模式的介绍及代码演示
组合模式是一种结构型设计模式,用于将多个对象组织成树形结构,并统一处理所有对象。例如,统计公司总人数时,可先统计各部门人数再求和。该模式包括一个通用接口、表示节点的类及其实现类。通过树形结构和节点的通用方法,组合模式使程序更易扩展和维护。
Java设计模式:组合模式的介绍及代码演示
|
3月前
|
设计模式 算法 PHP
PHP中的设计模式:策略模式的深入探索与实践在软件开发的广袤天地中,PHP以其独特的魅力和强大的功能,成为无数开发者手中的得力工具。而在这条充满挑战与机遇的征途上,设计模式犹如一盏明灯,指引着我们穿越代码的迷雾,编写出更加高效、灵活且易于维护的程序。今天,就让我们聚焦于设计模式中的璀璨明珠——策略模式,深入探讨其在PHP中的实现方法及其实际应用价值。
策略模式,这一设计模式的核心在于它为软件设计带来了一种全新的视角和方法。它允许我们在运行时根据不同情况选择最适合的解决方案,从而极大地提高了程序的灵活性和可扩展性。在PHP这门广泛应用的编程语言中,策略模式同样大放异彩,为开发者们提供了丰富的创作空间。本文将从策略模式的基本概念入手,逐步深入到PHP中的实现细节,并通过一个具体的实例来展示其在实际项目中的应用效果。我们还将探讨策略模式的优势以及在实际应用中可能遇到的挑战和解决方案,为PHP开发者提供一份宝贵的参考。
|
3月前
|
设计模式 存储 安全
Java设计模式-组合模式(13)
Java设计模式-组合模式(13)
|
3月前
|
设计模式 存储 算法
PHP中的设计模式:策略模式的深入解析与应用在软件开发的浩瀚海洋中,PHP以其独特的魅力和强大的功能吸引了无数开发者。作为一门历史悠久且广泛应用的编程语言,PHP不仅拥有丰富的内置函数和扩展库,还支持面向对象编程(OOP),为开发者提供了灵活而强大的工具集。在PHP的众多特性中,设计模式的应用尤为引人注目,它们如同精雕细琢的宝石,镶嵌在代码的肌理之中,让程序更加优雅、高效且易于维护。今天,我们就来深入探讨PHP中使用频率颇高的一种设计模式——策略模式。
本文旨在深入探讨PHP中的策略模式,从定义到实现,再到应用场景,全面剖析其在PHP编程中的应用价值。策略模式作为一种行为型设计模式,允许在运行时根据不同情况选择不同的算法或行为,极大地提高了代码的灵活性和可维护性。通过实例分析,本文将展示如何在PHP项目中有效利用策略模式来解决实际问题,并提升代码质量。
|
3月前
|
设计模式 缓存 算法
Java设计模式-访问者模式(22)
Java设计模式-访问者模式(22)
|
6月前
|
设计模式 存储 安全
Java设计模式:组合模式之透明与安全的两种实现(七)
Java设计模式:组合模式之透明与安全的两种实现(七)
|
6月前
|
设计模式 Java
Java设计模式之组合模式详解
Java设计模式之组合模式详解
|
6月前
|
设计模式
组合模式-大话设计模式
组合模式-大话设计模式