设计模式结构型模式

Posted weixin_42412601

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式结构型模式相关的知识,希望对你有一定的参考价值。

目录

结构型模式:解决的是,怎么让我们的软件更加的有伸缩性、扩展性。

1、适配器模式

基本介绍:
1)适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)
2)适配器模式属于结构型模式
3)主要分为三类:类适配器模式、对象适配器模式、接口适配器模式

类适配器模式

被适配者:220V电压

public class Voltage220V 
    //输出220v电压
    public int output220V()
        int src=220;
        System.out.println("电压="+src+"伏");
        return src;
    

适配接口:5V电压

public interface IVoltage5V 
    int output5V();

适配器:将220V电压转成5V,以便手机适配,能够进行充电

public class VoltageAdapter extends Voltage220V implements IVoltage5V
    @Override
    public int output5V() 
        //获取到220V电压
        int i = output220V();
        System.out.println("适配器正在将220V电压转为5V");
        //转成5V电压
        return i/44;
    

手机:

public class Phone 
    //充电
    public void charging(IVoltage5V iVoltage5V)
        if (iVoltage5V.output5V()==5)
            System.out.println("电压5V,可以充电");
        else
            System.out.println("电压不等于5V,不能充电");
        
    

使用者:

    public static void main(String[] args) 
        Phone phone = new Phone();
        phone.charging(new VoltageAdapter());
    

类适配器模式注意事项和细节
1)Java 是单继承机制,所以类适配器需要继承src[被适配者]类这一点算是一个缺点, 因为这要求dst[适配者]必须是接口,有一定局限性;
2)src 类的方法在 Adapter 中都会暴露出来,也增加了使用的成本。
3)由于其继承了 src 类,所以它可以根据需求重写 src 类的方法,使得 Adapter 的灵活性增强了。

对象适配器模式

对象适配器就是对类适配器模式的改进。
使用关联关系(聚合)来替代继承关系,遵循的是合成复用原则。

适配器

public class VoltageAdapter   implements IVoltage5V 
    private Voltage220V voltage220V;

    public VoltageAdapter(Voltage220V voltage220V) 
        this.voltage220V = voltage220V;
    

    @Override
    public int output5V() 
        //获取到220V电压
        int i = voltage220V.output220V();
        System.out.println("适配器正在将220V电压转为5V");
        //转成5V电压
        return i/44;
    

使用者:

    public static void main(String[] args) 
        Phone phone = new Phone();
        phone.charging(new VoltageAdapter(new Voltage220V()));
    

接口适配器模式

  • 核心思路:当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求
  • 适用于一个接口不想使用其所有的方法的情况。

接口:

public interface Interface4 
     void m1();
     void m2();
     void m3();
     void m4();

适配器:

public abstract class AbsAdapter implements Interface4
    @Override
    public void m1() 

    
    @Override
    public void m2() 

    
    @Override
    public void m3() 

    
    @Override
    public void m4() 

    

使用:

    public static void main(String[] args) 
        AbsAdapter absAdapter = new AbsAdapter() 
            //只需要去覆盖我们 需要使用的接口方法
            @Override
            public void m1() 
                System.out.println("使用了 m1 的方法");
            
        ;
        absAdapter.m1();
    

适配器模式在 SpringMVC 框架应用的源码剖析

1)SpringMvc 中的 HandlerAdapter, 就使用了适配器模式
2)SpringMVC 处理请求的流程回顾
3)使用 HandlerAdapter 的原因分析:
可以看到处理器的类型不同,有多重实现方式,那么调用方式就不是确定的,如果需要直接调用 Controller 方法,需要调用的时候就得不断是使用 if else 来进行判断是哪一种子类然后执行。那么如果后面要扩展 Controller, 就得修改原来的代码,这样违背了 OCP 原则。
4)代码分析+Debug 源码

2、桥接模式

1、手机操作问题

现在对不同手机类型的不同品牌实现操作编程(比如:开机、关机、上网、打电话等),如图:

2、传统方案解决收集操作问题

传统方法对应的类图

扩展性极差(会产生类爆炸),如果我们再增加手机的类型,就需要增加各个品牌手机的类,同样如果我们增加一个手机品牌,也要在各个手机样式类下增加,违反了单一职责原则,增加了代码维护成本。

3、桥接模式解决收集操作问题

使用桥接模式改进传统方式,让程序具有更好的扩展性,利用程序维护

品牌接口:

public interface Brand 
    void open();
    void close();
    void call();

public class XiaoMi implements Brand 
    @Override
    public void open() 
        System.out.println("小米手机开机");
    
    @Override
    public void close() 
        System.out.println("小米手机关机");
    
    @Override
    public void call() 
        System.out.println("小米手机打电话");
    

public class Vivo implements Brand 
    @Override
    public void open() 
        System.out.println("Vivo手机开机");
    
    @Override
    public void close() 
        System.out.println("Vivo手机关机");
    
    @Override
    public void call() 
        System.out.println("Vivo手机打电话");
    

手机抽象类:

public abstract  class Phone 
    //聚合品牌
    Brand brand;
    public Phone(Brand brand) 
        this.brand = brand;
    
    public void open() 
        brand.open();
    
    public void close() 
        brand.close();
    
    public void call() 
        brand.call();
    

public class FoldedPhone extends Phone
    public FoldedPhone(Brand brand) 
        super(brand);
    
    @Override
    public void open() 
        super.open();
        System.out.println("折叠样式手机");

    
    @Override
    public void close() 
        super.close();
        System.out.println("折叠样式手机");

    
    @Override
    public void call() 
        super.call();
        System.out.println("折叠样式手机");

    

使用方:

public class Client 
    public static void main(String[] args) 
        Brand xiaoMi = new XiaoMi();
        Phone phone = new FoldedPhone(xiaoMi);
        phone.open();
        phone.call();
        phone.close();
    

4、桥接模式在 JDBC 的源码剖析

Jdbc 的 Driver 接口,如果从桥接模式来看,Driver 就是一个接口,下面可以有 mysql 的 Driver,Oracle 的
Driver,这些就可以当做实现接口类

与桥接模式不完全相同

3、装饰者模式(也叫包装模式)

装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)

主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。
比如:
1)咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)
2)调料:Milk、Soy(豆浆)、Chocolate
3)要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便

使用继承的方式来实现:

每种单品咖啡都有对应调料,这样每种咖啡就会有很多与调料组成的子类,比如:加奶的咖啡,巧克力咖啡…
并且以后新出了一种咖啡,这个咖啡对应的与调料组成的子类也会很多。
问题:这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料,类的数量就会倍增,就会出现类爆炸

这种情况就可以使用装饰者模式解决,在不想增加很多子类的情况下扩展类功能。

例如:

1、被装饰者:

public interface Shape 
    void draw();

public class Circle implements Shape
    @Override
    public void draw() 
        System.out.println("draw circle");
    

public class Rectangle implements Shape
    @Override
    public void draw() 
        System.out.println("draw rectangle");
    

希望能够对被装饰者进行增强,但不是仅仅通过增加子类的方式去实现,因为可能会引起类爆炸的问题。

2、装饰者:实现被装饰者,聚合被装饰者

public abstract class ShapeWrapper implements Shape
    protected Shape shape;
    public ShapeWrapper(Shape shape) 
        this.shape = shape;
    
    @Override
    public void draw()
        shape.draw();
    

3、装饰者的具体实现类,即子类
主要是对被装饰者的增强

public class RedShapeWrapper extends ShapeWrapper
    public RedShapeWrapper(Shape shape) 
        super(shape);
    
    //对被装饰者的draw方法进行增强
    @Override
    public void draw() 
        shape.draw();
        //具体的增强
        setRedBorder(shape);
    
    private void setRedBorder(Shape decoratedShape)
        System.out.println("Border Color: Red");
    

4、使用

    public static void main(String[] args) 
        RedShapeWrapper redShapeWrapper = new RedShapeWrapper(new Circle());
        redShapeWrapper.draw();
    
draw circle
Border Color: Red

对被装饰者的增强,仅仅只需要在外面,再包一下,即可,这样就不用去添加更多的子类,来达到这个效果了。

3.1、装饰者模式在 JDK 应用的源码分析


FilterInputStream聚合了InputStream

public static void main(String[] args) throws Exception		
		//new FileInputStream是一个被装饰者
		//DataInputStream是一个装饰者,对FileInputStream进行功能上的增强
		DataInputStream dis = new DataInputStream(new FileInputStream("d:\\\\abc.txt")); 
		System.out.println(dis.read());
		dis.close();

4、组合模式

1、基本介绍
1)组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系。
2)组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
3)这种类型的设计模式属于结构型模式。
4)组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象

2、编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院, 一个学院有多个系

传统方案解决学校院系展示存在的问题分析
1)将学院看做是学校的子类,系是学院的子类,这样实际上是站在组织大小来进行分层次的
2)实际上我们的要求是 :在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系, 因
此这种方案,不能很好实现的管理的操作,比如对学院、系的添加,删除,遍历等
3)解决方案:把学校、院、系都看做是组织结构,他们之间没有继承的关系,而是一个树形结构,可以更好的实现管理操作。 => 组合模式

3、组合模式原理类图:

1)Component :这是组合中对象声明接口,在适当情况下,实现所有类共有的接口默认行为,用于访问和管理
Component 子部件, Component 可以是抽象类或者接口
2)Leaf : 在组合中表示叶子节点,叶子节点没有子节点
3)Composite :非叶子节点, 用于存储子部件, 在 Component 接口中实现 子部件的相关操作,比如增加(add), 删除。

4、组合模式解决学校院系展示的应用实例:

5、代码实现
Component:

@Getter
@Setter
public abstract class OrganizationComponent 
    //名称
    private String name;
    //描述
    private String desc;
    public OrganizationComponent(String name, String desc) 
        this.name = name;
        this.desc = desc;
    
    //默认实现,因为子节点,不能add
    public void add(OrganizationComponent organizationComponent)
        throw new UnsupportedOperationException();
    
    //默认实现,因为子节点,不能add
    public void remove(OrganizationComponent organizationComponent)
        throw new UnsupportedOperationException();
    
    //方法 print,  做成抽象的,  子类都需要实现
    protected abstract void print();

Composite非叶子节点:

//University 就是 Composite ,  可以管理 College
public class UniversityComposite extends OrganizationComponent
    //大学管理多个College
    List<OrganizationComponent> organizationComponents=new ArrayList<OrganizationComponent>();
    public UniversityComposite(String name, String desc) 
        super(name, desc);
    
    //添加学院
    @Override
    public void add(OrganizationComponent organizationComponent) 
        organizationComponents.add(organizationComponent);
    
    //删除学院
    @Override
    public void remove(OrganizationComponent organizationComponent) 
        organizationComponents.remove(organizationComponent);
    
    // print 方法,就是输出 University  包含的学院
    @Override
    protected void print() 
        System.out.println("--------------" + getName() + "--------------");
        //遍历 organizationComponents
        for (OrganizationComponent organizationComponent : organizationComponents) 
            organizationComponent.print();
        
    

//College 就是 Composite ,  可以管理Department
public class College extends OrganizationComponent
    //List中存放的 Department
    List<OrganizationComponent> organizationComponents=new ArrayList<OrganizationComponent>();
    public College(String name, String desc) 
        super(name, desc);
    
    //添加系Department
    @Override
    public void add(OrganizationComponent organizationComponent) 
        organizationComponents.add(organizationComponent);
    
    //删除系Department
    @Override
    public void remove(OrganizationComponent organizationComponent) 
        organizationComponents.remove(organizationComponent);
    

    // print 方法,就是输出College包含的系
    @Override
    protected void print() 
        System.out.println("--------------" + getName() + "--------------");
        //遍历 organizationComponents
        for (OrganizationComponent organizationComponent : organizationComponents) 
            organizationComponent.print();
        
    

//Department就是Composite
public class Department extends OrganizationComponent
    //系已经是最小的了,没有下一层了,它自己就是叶子节点
    //所以没有集合了

    public Department(String name, String desc) 
        super(name, desc);
    

    //add , remove 就不用写了,因为他是叶子节点

    @Override
    protected void print() 
        System.out.println(getName());
    

使用者:

public static void main(String[] args) 
    //从大到小创建对象,大学
    OrganizationComponent universityComposite = new UniversityComposite("清华大学","清华大学描述xxx");

    //创建学院
    OrganizationComponent computerCollege  = new College("计算机学院","计算机学院描述xxx");
    OrganizationComponent infoEngineerCollege  = new College("信息工程学院","信息工程学院描述xxx");

    //创建计算机学院下面的系并添加到学院里
    computerCollege.add(new Department("软件工程","软件工程不错"));
    computerCollege.add(new Department("网络工程","网络工程不错"));
    computerCollege.add(new Department("计算机科学与技术", " 计算机科学与技术是老牌的专业 "));

    //创建信息工程学院下面的系并添加到学院里
    infoEngineerCollege.add(new Department("通用工程","通用工程xxx"));
    infoEngineerCollege.add(new Department("信息工程","信息工程好学"));

    //将学院加入到学校
    universityComposite.add(computerCollege);
    universityComposite.add(infoEngineerCollege);

    //查看大学里的信息
    universityComposite.print();
    System.out.println("=======================================");
    //查看某个学院的信息
    computerCollege.print();

4.1、组合模式在 JDK 集合的源码分析

Java 的集合类-HashMap 就使用了组合模式

  • Map/AbstractHashMap就是一个抽象的构建(相当于component)
  • HashMap是一个中间的构建(Composite),实现/继承了相关方法put/putall
  • Node是HashMap的静态内部类,类似leaf叶子节点,这里就没有put/putall

5、外观模式(门面模式)

基本介绍
1)外观模式(Facade),也叫“过程模式:外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
2)外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节
主要解决:降低访问复杂系统的内部子系统时的复杂度,简化客户端与之的接口。

例如:

接口和实现类:

public interface Shape 
   void draw();

public class Rectangle implements Shape 
   @Override
   public void draw() 
      System.out.println("Rectangle::draw()");
   

public class Square implements Shape 
   @Override
   public void draw() 
      System.out.println("Square::draw()");
   

public class Circle implements Shape 
   @Override
   public void draw() 
      System.out.println("Circle::draw()");
   

创建一个外观类。

public class ShapeMaker 
   private Shape circle;
   private Shape rectangle;
   private Shape square;
 
   public ShapeMaker() 
      circle = new Circle();
      rectangle = new Rectangle();
      square = new Square();
   
 
   public void drawCircle()
      circle.draw();
   
   public void drawRectangle()
      rectangle.draw();
   
   public void drawSquare()
      square.draw();
   

使用者:使用者直接使用外观类,无需关心内部子系统是如何实现的。

   public static void main(String[] args) 
      ShapeMaker shapeMaker = new ShapeMaker();
 
      shapeMaker.drawCircle();
      shapeMaker.drawRectangle();
      shapeMaker.drawSquare();      
   

5.1、外观模式在 MyBatis 框架应用的源码分析

Configuration相当于外观类:

DefaultReflectorFactory、DefaultObjectFactory、DefaultObjectWrapperFactory是组合在外观类中的子系统。

Configuration外观类提供了newMetaObject方法供客户端使用,屏蔽了内部对子系统的细节,降低了客户端对子系统使用的复杂性

6、享元模式

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。

主要解决:在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。

使用场景: 1、系统有大量相似对象。 2、需要缓冲池的场景。

应用实例: 1、JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面。 2、数据库的数据池。


接口和实现类:

public interface Shape 
   void draw();

public class Circle implements Shape 
   private String color;
   private int x;
   private int y;
   private int radius;
 
   public Circle(String color)
      this.color = color;     
   
 
   public void setX(int x) 
      this.x = x;
   
 
   public void setY(int y) 
      this.y = y;
   
 
   public void setRadius(int radius) 
      this.radius = radius;
   
 
   @Override
   public void draw() 
      System.out.println("Circle: Draw() [Color : " + color 
         +", x : " + x +", y :" + y +", radius :" + radius);
   

工厂类:

public class ShapeFactory 
    //用于缓存大量相似对象
    private static final Map<String,Shape> circleMap=new HashMap<>();
    //获取对象
    public static Shape getCircle(String color)
        Circle  circle = (Circle) circleMap.get(color);
        if (circle==null)
            circle=new Circle(color);
            circleMap.put(color,circle);
            System.out.println("Creating circle of color : " + color);
        
        return circle;
    

使用:

public class FlyweightPatternDemo 
   private static final String colors[] = 
       "Red", "Green", "Blue", "White", "Black" ;
   public static void main(String[] args) 
 
      for(int i=0; i < 20; ++i) 
         Circle circle = 
            (Circle)ShapeFactory.getCircle(getRandomColor());
         circle.setX(getRandomX());
         circle.setY(getRandomY());
         circle.setRadius(100);
         circle.draw();
      
   
   private static String getRandomColor() 
      return colors[(int)(Math.random()*colors.length)];
   
   private static int getRandomX() 
      return (int)(Math.random()*100 );
   
   private static int getRandomY() 
      return (int)(Math.random()*100以上是关于设计模式结构型模式的主要内容,如果未能解决你的问题,请参考以下文章

第三式 装饰者模式

Java进阶篇设计模式之四 -----适配器模式和桥接模式

设计模式--装饰者模式

设计模式---结构型模式之适配器模式(Adapter Pattern)

设计模式第5篇:适配器模式

Java设计模式之装饰者模式