设计模式-结构型模式讲解上(适配器、桥接、组合、享元)

举报
程序员-上善若水 发表于 2022/06/23 23:40:27 2022/06/23
【摘要】 一、结构型设计模式 上篇,我们呢讲解了创建型设计模式,包括 单例、原型、工厂方法,抽象工厂、构建者模式。 文章地址:https://blog.csdn.net/qq_43692950/artic...

一、结构型设计模式

上篇,我们呢讲解了创建型设计模式,包括 单例、原型、工厂方法,抽象工厂、构建者模式。

文章地址:https://blog.csdn.net/qq_43692950/article/details/120165779

这篇文章我们来讲解下结构型设计模式,结构型设计模式,主要处理类或对象的组合关系,为如何设计类以形成更大的结构提供指南。

结构型设计模式包括:适配器模式(Adapter Pattern)、桥接模式(Bridge Pattern)、组合模式(Composite Pattern)、装饰器模式(Decorator Pattern)、外观模式(Facade Pattern)、享元模式(Flyweight Pattern)、代理模式(Proxy Pattern)

在本文中主要介绍 适配器、桥接、组合、享元四种设计模式,下篇文章为讲解 外观、代理、 装饰器 。

二、适配器模式

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。它结合了两个独立接口的功能。
优点:1、可以让任何两个没有关联的类一起运行。 2、提高了类的复用。 3、增加了类的透明度。 4、灵活性好。

这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,在视频播放器中,假设视频播放器只能播放MP4格式的视频,那现在又个VLC格式的视频,那就不能播放了,那要怎么办呢?如果我们做个装换器,将VLC格式的视频转换为MP4格式的视频不就可以播放了吗,那这个转换器我们就可以采用适配器设计模式来设计。

下面使用程序演示下上面的例子:

  1. 定义视频接口
public interface VideoInterFace {
    String getVideoPath();
}
  1. 定时Mp4格式视频实例
public class Mp4Video implements VideoInterFace {
    @Override
    public String getVideoPath() {
        return "Mp4视频的路径";
    }
}
  1. 定义Vlc格式视频实例
public class VlcVideo implements VideoInterFace{
    @Override
    public String getVideoPath() {
        return "Vlc视频的路径";
    }
}
  1. 定义播放器,只接口Mp4格式的视频
public class Player {
    private Mp4Video video;

    public Player(Mp4Video video) {
        this.video = video;
    }

    public void play() {
        System.out.println(StringFormatter.concat("播放视频视频地址:", video.getVideoPath()).getValue());
    }
}
  1. 我们需要播放Vlc格式的视频,定义Mp4的适配器,并接收Vlv格式视频,进行转码。
public class Mp4Adapter extends Mp4Video {

    private VlcVideo vlcVideo;

    public Mp4Adapter(VlcVideo vlcVideo) {
        this.vlcVideo = vlcVideo;
    }

    @Override
    public String getVideoPath() {
        System.out.println(StringFormatter.concat("开始格式转换,vlc地址:", vlcVideo.getVideoPath()).getValue());
        return "转换后的Mp4路径!";
    }
}
  1. 测试
public class demo {
    public static void main(String[] args) {
        Player player = new Player(new Mp4Video());
        player.play();

        VlcVideo vlcVideo = new VlcVideo();
        Player player1 = new Player(new Mp4Adapter(vlcVideo));
        player1.play();
    }
}

在这里插入图片描述
从上面的例子可以看出,需要播放Vlc格式,我们就需要写一个目标适配器,这里是Mp4适配器,并继承Mp4,使之有Mp4的特性,并在内部做相应的转换即可,提高了系统的可扩展性。

三、桥接模式

桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。
这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。
优点: 1、抽象和实现的分离。 2、优秀的扩展能力。 3、实现细节对客户透明。

举个例子:绘画不同颜色的各种图像,画不同的形状和涂颜色,便是两个不同的功能,但两者又相互联系,在画完形状后需要涂颜色,但颜色和形状有使多种多样的,此时就可以采用桥接设计模式,将两者的抽象化与实现化解耦,形状和颜色可以独立变化。

下面使用程序演示下上面的例子:

  1. 定义颜色的接口
public interface ColorApi {
   public void drawCircle();
}
  1. 定义不同颜色的实现,这里采用红色和绿色
public class ReqColor implements ColorApi {
    @Override
    public void drawCircle() {
        System.out.println("开始涂红色!");
    }
}
public class GreenColor implements ColorApi {
   @Override
   public void drawCircle() {
      System.out.println("开始涂绿色!");
   }
}
  1. 定义形状的接口
public interface ShapeApi {
    //画形状
    void draw();
    //画形状并涂颜色
    void drawShapeAndsColor();
}
  1. 定义形状的抽象模板,将共性的操作定义到抽象中
public abstract class ShapeAbstract implements ShapeApi {

    public ColorApi colorApi;

    public ShapeAbstract(ColorApi colorApi) {
        this.colorApi = colorApi;
    }

    @Override
    public void drawShapeAndsColor() {
        draw();
        colorApi.drawCircle();
    }
}
  1. 定义圆形的实例
public class Circle extends ShapeAbstract {

   public Circle(ColorApi colorApi) {
      super(colorApi);
   }
 
   @Override
   public void draw() {
      System.out.println("开始画圆形!");
   }
}
  1. 定义矩形的实例
public class Rectangle extends ShapeAbstract {

    public Rectangle(ColorApi colorApi) {
        super(colorApi);
    }

    @Override
    public void draw() {
        System.out.println("开始画矩形");
    }
}
  1. 演示
public class demo {
   public static void main(String[] args) {
      ShapeApi shapeReq = new Circle(new ReqColor());
      shapeReq.drawShapeAndsColor();

      ShapeApi shapeGreen = new Circle(new GreenColor());
      shapeGreen.drawShapeAndsColor();

      ShapeApi rectangle = new Rectangle(new GreenColor());
      rectangle.drawShapeAndsColor();
   }
}

在这里插入图片描述
上面可以看出,可以灵活的定义形状和颜色的组合,并且他们两个都可以独立变化,添加新的形状只需,建立新的类并实现形状接口,添加颜色也是如此,极大的提高的系统的可扩展性和可维护型。

四、组合模式

组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。
组合模式依据树形结构来组合对象,用来表示部分以及整体层次。它创建了对象组的树形结构。
优点: 1、高层模块调用简单。 2、节点自由增加。
缺点:在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。

举个例子:一个公司,从上到下分为,公司、部门、小组等,他们整个在一起才能称为一个完整的公司,要表示做个公司的结构,就可以采用组合设计模式。

下面使用程序演示下上面的例子:

  1. 定义属性类,用来表示不同层级的对象
@Data
public class Property {
   private String name;
   //下一层的子集
   private List<Property> next;

   public Property(String name) {
      this.name = name;
      next = new ArrayList<Property>();
   }
 
   public void add(Property e) {
      next.add(e);
   }
 
   public void remove(Property e) {
      next.remove(e);
   }
 
   public List<Property> getSubordinates(){
     return next;
   }
}
  1. 使用演示
public class demo {
   public static void main(String[] args) {
      Property company = new Property("公司");
      Property department = new Property("部门");
      Property group = new Property("小组");

      company.add(department);
      department.add(group);

      System.out.println(company);
   }
}

在这里插入图片描述
上面就演示了一个公司的组合,通过company对象就可以获得整个公司的各个部分的对象。组合设计模式主要适合于整体部分的场景。

五、享元模式

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。它提供了减少对象数量从而改善应用所需的对象结构的方式。
享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。他的优点是大大减少对象的创建,降低系统的内存,使效率提高,但也有可能造成内存的浪费。比如Spring的采用容器的方式存储bean,使用时从容器中获取。

还是拿上面的画不同颜色形状的例子演示下享元设计模式的使用:

  1. 定义形状的接口
public interface Shape {
   void draw();
}
  1. 定义圆形的实现,并接收一个颜色值:
public class Circle implements Shape {

    private String color;

    public Circle(String color) {
        this.color = color;
    }

    @Override
    public void draw() {
        System.out.println(StringFormatter.concat("开始画 ", color, " 色的圆 ").getValue());
    }
}
  1. 定义形状对象获取工厂,根据颜色值将对象存储到HashMap中,后再根据颜色值取对象,达到复用的效果。
public class ShapeFactory {

    private static final HashMap<String, Shape> circleMap = new HashMap<>();

    public static Shape getCircle(String color) {
        if (!circleMap.containsKey(color)) {
            System.out.println(StringFormatter.concat(">> 创建", color, "颜色的圆 ").getValue());
            circleMap.put(color, new Circle(color));
        }
        return circleMap.get(color);
    }
}
  1. 使用
public class demo {
    private static final String colors[] =
            {"Red", "Green", "Blue", "White", "Black"};

    public static void main(String[] args) {

        for (int i = 0; i < 20; ++i) {
            Shape circle = ShapeFactory.getCircle(getRandomColor());
            circle.draw();
        }
    }

    private static String getRandomColor() {
        return colors[(int) (Math.random() * colors.length)];
    }
}

在这里插入图片描述
上面可以看出,享元设计模式可以大大减少对象的创建,但也有可以造成内存的浪费,比如某个对象的使用频率非常低,如果一直存在内存中就有点浪费空间了。

文章来源: blog.csdn.net,作者:小毕超,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/qq_43692950/article/details/120248267

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。