Skip to content

组合模式构建商品类目

概述

将对象组合成树形结构以表示"部分-整体"的层次结构。"Composite 使得用户对单个对象和组合对象的使用具有一致性。

定义 Component 组件

声明一个接口用于访问和管理 Component 的子组件

java
@NoArgsConstructor
public class AbstractProductItem {

    protected void addProductItem(AbstractProductItem item) {
        throw new UnsupportedOperationException("Not Support child add");
    }

    protected void delProductChild(AbstractProductItem item) {
        throw new UnsupportedOperationException("Not Support child del");
    }
}

Composite 树枝组件

Composite 定义子部件的行为。 存储子部件。 在 Component 接口中实现与子部件有关的操作。

java
package com.ssn.design.patterns.service.product.compose;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * Composite 树枝组件
 */
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ProductComposite extends AbstractProductItem{

    private Integer id;

    private String name;

    private Integer pid;

    private List<AbstractProductItem> child = new ArrayList<>();

    /**
     * 新增商品类目
     * @param item
     */
    @Override
    public void addProductItem(AbstractProductItem item) {
        this.child.add(item);
    }

    /**
     * 移除商品类目
     * @param item
     */
    @Override
    public void delProductChild(AbstractProductItem item) {
        ProductComposite removeItem = (ProductComposite)item;
        Iterator iterator = child.iterator();
        while (iterator.hasNext()) {
            ProductComposite composite = (ProductComposite)iterator.next();
            if (composite.getId().equals(removeItem.getId())) {
                iterator.remove();
                break;
            }
        }
    }


    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getPid() {
        return pid;
    }

    public void setPid(Integer pid) {
        this.pid = pid;
    }

    public List<AbstractProductItem> getChild() {
        return child;
    }

    public void setChild(List<AbstractProductItem> child) {
        this.child = child;
    }
}

Client 使用

java
package com.ssn.design.patterns.service.product;

import com.ssn.design.patterns.cache.RedisCommonProcessor;
import com.ssn.design.patterns.pojo.ProductItem;
import com.ssn.design.patterns.repo.ProductItemRepository;
import com.ssn.design.patterns.service.product.compose.AbstractProductItem;
import com.ssn.design.patterns.service.product.compose.ProductComposite;
import com.ssn.design.patterns.service.product.vistor.AddItemVisitor;
import com.ssn.design.patterns.service.product.vistor.DelItemVisitor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class ProductItemService {

    private final RedisCommonProcessor redisCommonProcessor;

    private final ProductItemRepository productItemRepository;

    private final AddItemVisitor addItemVisitor;

    private final DelItemVisitor delItemVisitor;

    public ProductItemService(RedisCommonProcessor redisCommonProcessor, ProductItemRepository productItemRepository, AddItemVisitor addItemVisitor, DelItemVisitor delItemVisitor) {
        this.redisCommonProcessor = redisCommonProcessor;
        this.productItemRepository = productItemRepository;
        this.addItemVisitor = addItemVisitor;
        this.delItemVisitor = delItemVisitor;
    }

    public ProductComposite fetchAllItems() {
        Object cacheItems = redisCommonProcessor.get("items");
        if (cacheItems != null) {
            return (ProductComposite) cacheItems;
        }
        List<ProductItem> fetchDbItems = productItemRepository.findAll();
        ProductComposite items = genegateProductTree(fetchDbItems);
        if (items == null) {
            throw new UnsupportedOperationException("Product Items should not be in DB");
        }
        redisCommonProcessor.set("items",items);
        return items;
    }


    private ProductComposite genegateProductTree(List<ProductItem> fetchDbItems) {
        List<ProductComposite> composites = new ArrayList<>();
        fetchDbItems.forEach(dbItem -> {
            composites.add(ProductComposite.builder()
                    .id(dbItem.getId()).name(dbItem.getName()).pid(dbItem.getPid())
                    .build()
            );
        });
        Map<Integer, List<ProductComposite>> groupingList = composites.stream().collect(Collectors.groupingBy(ProductComposite::getPid));
        composites.stream().forEach(item -> {
            List<ProductComposite> list = groupingList.get(item.getId());
            item.setChild(list == null ? new ArrayList<>() : list.stream().map(x -> (AbstractProductItem)x).collect(Collectors.toList()));
        });
        ProductComposite composite = composites.size() == 0 ? null : composites.get(0);
        return composite;
    }

}

Released under the MIT License.