设计模式模型与算法

设计模式-组合模式(结构型)

2019-08-13  本文已影响8人  NealLemon

定义

适用场景

优点

缺点

代码

相信很多小伙伴都是车迷,都想拥有一辆属于自己的汽车,但是在我们选购汽车的时候,肯定是根据分类来选择的。下面让我们拿选择宝马汽车来作为一个业务例子,展示组合模式的概念。

我们都知道宝马汽车分为国产和进口,又分为1系,3系,5系,7系,每个系列中有不同的车型来供消费者选择。我们就以这个为例,来以组合模式表示出来。

首先我们需要一个目录组件(catalogComponent

这个组件来规定一些车系和车型都需要的内容

/**
 * 公共内容的抽象类
 */
public abstract class CatalogComponent {
    
    /**
     * 增加车型
     * @param carModel
     */
    public void addCarModel(CatalogComponent carModel) {
        throw new UnsupportedOperationException("不支持添加车型");
    }

    /**
     * 获取名称
     * @return
     */
    public String getName() {
        throw new UnsupportedOperationException("不支持获取名称");
    }


    /**
     * 获取价格
     * @return
     */
    public String getPrice() {
        throw new UnsupportedOperationException("不支持获取价格");
    }

    /**
     * 打印内容
     * @return
     */
    public void print() {
        throw new UnsupportedOperationException("不支持添加打印");
    }

}

这里有目录结构需要的添加车型方法,也有车型需要的价格方法,其他的是公共内容。

车型目录类(BMWCarCatalog)

/**
 * BMW车型目录
 */
public class BMWCarCatalog extends CatalogComponent {

    List<CatalogComponent> bmwCars = new ArrayList<>();
    private String name;
    //层级
    private Integer level;

    public BMWCarCatalog(String name, Integer level) {
        this.name = name;
        this.level = level;
    }

    @Override
    public void addCarModel(CatalogComponent carModel) {
        bmwCars.add(carModel);
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public void print() {
        /**
         * 为了打印目录结构好看做的优化
         */
        System.out.println(name);
        for (CatalogComponent bmwCar : bmwCars) {
            if(level != null) {
                for(int i = 0 ; i < level ; i++) {
                    System.out.print(" ");
                }
            }
            bmwCar.print();
        }
    }
}

因为是目录,我们只需要 namelevel两个变量。同时 只需要重写 addCarModel(),getName()print()方法。

车型类(BMWCar)

/**
 * 车型类
 */
public class BMWCar extends CatalogComponent {

    private String name;
    private String price;
    //年份
    private String year;
    //层级
    private Integer level;

    public BMWCar(String name, String price, String year, Integer level) {
        this.name = name;
        this.price = price;
        this.year = year;
        this.level = level;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public String getPrice() {
        return this.price;
    }

    @Override
    public void print() {
        if(level != null) {
            for(int i = 0 ; i < level ; i++) {
                System.out.print(" ");
            }
        }
        System.out.println(year + "年款, 车型名称:" + name + ",价格为:"+ price);
    }
}

这里需要所有的变量 包括名称 年限 价格 和层级。同时重写了 getPrice(),getName()print()方法。

测试类

简单的组合对应的类已经完成了,那么现在让我们完成这么一个目录的输出

代码示例

就是简单的层级封装。

public class CombinationBootStrap {

    public static void main(String[] args) {
        //创建国产宝马
        CatalogComponent madeInChina = new BMWCarCatalog("国产宝马",1);
        //创建进口宝马
        CatalogComponent importBMW= new BMWCarCatalog("进口宝马",1);
        /**
         * 创建国产车系
         */
        //1系目录
        CatalogComponent oneEr = new BMWCarCatalog("1系",2);
        //3系目录
        CatalogComponent threeEr = new BMWCarCatalog("3系",2);
        //5系目录
        CatalogComponent fiveEr = new BMWCarCatalog("5系",2);
        /**
         * 创建进口车系
         */
        CatalogComponent sevenEr = new BMWCarCatalog("7系",2);
        /**
         * 创建车型
         */
        //一系
        CatalogComponent oneEr1 = new BMWCar("118i 时尚型","19.88万","2019年",3);
        CatalogComponent oneEr2 = new BMWCar("120i 领先型M运动套装","24.38万","2018年",3);
        oneEr.addCarModel(oneEr1);
        oneEr.addCarModel(oneEr2);
        //三系
        CatalogComponent threeEr1 = new BMWCar("325i M运动套装","31.39万 ","2020年",3);
        CatalogComponent threeEr2 = new BMWCar("320Li 时尚型 ","29.68万起","2019年",3);
        threeEr.addCarModel(threeEr1);
        threeEr.addCarModel(threeEr2);
        //五系
        CatalogComponent fiveEr1 = new BMWCar("525Li 豪华套装","42.69万 ","2019年",3);
        CatalogComponent fiveEr2 = new BMWCar("530Li 领先型 M运动套装 ","46.39万","2019年",3);
        fiveEr.addCarModel(fiveEr1);
        fiveEr.addCarModel(fiveEr2);
        //添加进国产宝马目录
        madeInChina.addCarModel(oneEr);
        madeInChina.addCarModel(threeEr);
        madeInChina.addCarModel(fiveEr);

        //七系
        CatalogComponent sevenEr1 = new BMWCar("730Li 豪华套装","82.80万 ","2019年",3);
        CatalogComponent sevenEr2 = new BMWCar("740Li 尊享型 M运动套装 ","106.80万 ","2019年",3);
        sevenEr.addCarModel(sevenEr1);
        sevenEr.addCarModel(sevenEr2);
        //添加进口车车系目录
        importBMW.addCarModel(sevenEr);

        /**
         * 创建宝马
         */
        CatalogComponent bmw = new BMWCarCatalog("宝马汽车",null);
        bmw.addCarModel(madeInChina);
        bmw.addCarModel(importBMW);
        bmw.print();
    }
}

查看输出

bmwoutput.jpg

其他源码使用

Spring Boot中 内容协商相关的类ContentNegotiationManager

我们只看特点突出的部分代码即可。

public class ContentNegotiationManager implements ContentNegotiationStrategy, MediaTypeFileExtensionResolver {

   private final List<ContentNegotiationStrategy> strategies = new ArrayList<>();
   //省略部分代码 ......
}

我们可以看到 ContentNegotiationManager中有一个List<ContentNegotiationStrategy> 变量,这与我们上面的demo是否很像呢。

我们接下来看一下ContentNegotiationStrategy接口的几个方法即可。

@FunctionalInterface
public interface ContentNegotiationStrategy {
   List<MediaType> resolveMediaTypes(NativeWebRequest webRequest)
         throws HttpMediaTypeNotAcceptableException;

}

由接口的方法我们可以确定,在ContentNegotiationManager一定重写了对应的方法,同时在List<ContentNegotiationStrategy>数据结构中的每一个也重写了不同的内容。我们就举几个类来看一下就够了。

**ContentNegotiationManager类中的:**

org.springframework.web.accept.ContentNegotiationManager#resolveMediaTypes

@Override
public List<MediaType> resolveMediaTypes(NativeWebRequest request) throws HttpMediaTypeNotAcceptableException {
   for (ContentNegotiationStrategy strategy : this.strategies) {
      List<MediaType> mediaTypes = strategy.resolveMediaTypes(request);
      if (mediaTypes.equals(MEDIA_TYPE_ALL_LIST)) {
         continue;
      }
      return mediaTypes;
   }
   return MEDIA_TYPE_ALL_LIST;
}

PathExtensionContentNegotiationStrategy类中的:

org.springframework.web.accept.PathExtensionContentNegotiationStrategy#getMediaTypeForResource

@Nullable
public MediaType getMediaTypeForResource(Resource resource) {
   Assert.notNull(resource, "Resource must not be null");
   MediaType mediaType = null;
   String filename = resource.getFilename();
   String extension = StringUtils.getFilenameExtension(filename);
   if (extension != null) {
      mediaType = lookupMediaType(extension);
   }
   if (mediaType == null) {
      mediaType = MediaTypeFactory.getMediaType(filename).orElse(null);
   }
   return mediaType;
}

小结

组合模式在Spring中运用的相当多,同时也可以看到这个模式运用的场景相当广泛。

上一篇下一篇

猜你喜欢

热点阅读