23种设计模式——结构型模式(上半部分)

23种设计模式——结构型模式(上半部分)

适配器模式(Adapter Pattern)

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。

这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。

介绍

意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

**主要解决:**主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。

何时使用:

  1. 系统需要使用现有的类,而此类的接口不符合系统的需要。
  2. 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。
  3. 通过接口转换,将一个类插入另一个类系中。

**如何解决:**继承或依赖(推荐)。

**关键代码:**适配器继承或依赖已有的对象,实现想要的目标接口。

应用实例:

  1. 美国电器 110V,中国 220V,就要有一个适配器将 110V 转化为 220V。
  2. JAVA JDK 1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator 接口,想要使用 1.2 的 JDK,则要将以前系统的 Enumeration 接口转化为 Iterator 接口,这时就需要适配器模式。
  3. 在 LINUX 上运行 WINDOWS 程序。
  4. JAVA 中的 jdbc。

优点:

  1. 可以让任何两个没有关联的类一起运行。
  2. 提高了类的复用。
  3. 增加了类的透明度。
  4. 灵活性好。

缺点:

1、过多地使用适配器,会让系统非常零乱,不易整体进行把握。

2.由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。

**使用场景:**有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。

**注意事项:**适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。

实现

  1. 我们有一个 MediaPlayer 接口和一个实现了 MediaPlayer 接口的实体类 AudioPlayer。默认情况下,AudioPlayer 可以播放 mp3 格式的音频文件。
  2. 我们还有另一个接口 AdvancedMediaPlayer 和实现了 AdvancedMediaPlayer 接口的实体类。该类可以播放 vlc 和 mp4 格式的文件。
  3. 我们想要让 AudioPlayer 播放其他格式的音频文件。为了实现这个功能,我们需要创建一个实现了== MediaPlayer 接口==的适配器类 MediaAdapter,并使用 AdvancedMediaPlayer 对象来播放所需的格式。
  4. AudioPlayer 使用适配器类 MediaAdapter 传递所需的音频类型,不需要知道能播放所需格式音频的实际类。==Client ==类使用 AudioPlayer 类来播放各种格
    23种设计模式——结构型模式(上半部分)_第1张图片

AdvancedMediaPlayer及其子类Mp4 Vlc

public interface AdvancedMediaPlayer {
    public void playVlc(String file);
    public void playMp4(String file);
}

class Mp4 implements AdvancedMediaPlayer{
    @Override
    public void playVlc(String file) {}
    
    @Override
    public void playMp4(String file) {
        System.out.println("Playing mp4--"+"fileName: "+file);
    }
}

class Vlc implements AdvancedMediaPlayer{
    @Override
    public void playVlc(String file) {
        System.out.println("Playing vlc--"+"fileName: "+file);
    }

    @Override
    public void playMp4(String file) {}
}

MediaPlayer和子类MediaAdapter适配器 子类 OldPlayer

public interface MediaPlayer {
    public void play(String type,String file);
}

class MediaAdapter implements MediaPlayer{
    private AdvancedMediaPlayer advancedMediaPlayer;

    public MediaAdapter(String type){
        if(type.equalsIgnoreCase("mp4")){
            advancedMediaPlayer = new Mp4();
        } else if (type.equalsIgnoreCase("vlc")) {
            advancedMediaPlayer = new Vlc();
        }else{
            System.out.println("该格式不支持");
        }
    }
    @Override
    public void play(String type, String file) {
        if(type.equalsIgnoreCase("mp4")){
            advancedMediaPlayer.playMp4(file);
        }else if (type.equalsIgnoreCase("vlc")){
            advancedMediaPlayer.playVlc(file);
        }else{
            System.out.println("该格式不支持"+type);
        }
    }
}

class OldPlayer implements MediaPlayer{
    private MediaAdapter mediaAdapter;

    @Override
    public void play(String type, String file) {
        if(type.equalsIgnoreCase("mp3")){//支持格式
            System.out.println("Playing mp3--"+"fileName: "+file);
        } else if (type.equalsIgnoreCase("mp4") || type.equalsIgnoreCase("vlc") ) {
            mediaAdapter = new MediaAdapter(type);
            mediaAdapter.play(type,file);
        }else {
            System.out.println("此格式不支持"+type);
        }
    }
}

Client用户端调用

public class Client extends OldPlayer {
    public static void main(String[] args) {
        OldPlayer oldPlayer = new OldPlayer();
        oldPlayer.play("mp3","a.txt");
        oldPlayer.play("mp4","a.txt");
        oldPlayer.play("vlc","a.txt");
        oldPlayer.play("error","a.txt");
    }
}

MVC中的应用

DispatcherServlet中的doDispatch方法,是将请求分发到具体的controller,因为存在很多不同类型的controller,常规处理是用大量的if…else…,来判断各种不同类型的controller,如下这样
23种设计模式——结构型模式(上半部分)_第2张图片









桥接模式(Bridge Pattern)

桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。

这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类,这两种类型的类可被结构化改变而互不影响。

桥接模式的目的是将抽象与实现分离,使它们可以独立地变化,该模式通过将一个对象的抽象部分与它的实现部分分离,使它们可以独立地改变。它通过组合的方式,而不是继承的方式,将抽象和实现的部分连接起来。

我们通过下面的实例来演示桥接模式(Bridge Pattern)的用法。其中,可以使用相同的抽象类方法但是不同的桥接实现类,来画出不同颜色的圆。

介绍

**意图:**将抽象部分与实现部分分离,使它们都可以独立的变化。

**主要解决:**在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。

**何时使用:**实现系统可能有多个角度分类,每一种角度都可能变化。

**如何解决:**把这种多角度分类分离出来,让它们独立变化,减少它们之间耦合。

**关键代码:**抽象类依赖实现类。

优点: 1、抽象和实现的分离。 2、优秀的扩展能力。 3、实现细节对客户透明。

**缺点:**桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。

使用场景:

  1. 如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
  2. 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
  3. 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。

**注意事项:**对于两个独立变化的维度,使用桥接模式再适合不过了。

以下是桥接模式的几个关键角色:

  • 抽象(Abstraction):定义抽象接口,通常包含对实现接口的引用。
  • 扩展抽象(Refined Abstraction):对抽象的扩展,可以是抽象类的子类或具体实现类。
  • 实现(Implementor):定义实现接口,提供基本操作的接口。
  • 具体实现(Concrete Implementor):实现实现接口的具体类。

实现

我们有一个作为桥接实现的 DrawAPI 接口和实现了 DrawAPI 接口的实体类RedCircleGreenCircleShape 是一个抽象类,将使用 DrawAPI 的对象。Client类使用 Shape 类来画出不同颜色的圆。
23种设计模式——结构型模式(上半部分)_第3张图片

**创建桥接实现接口DrawAPI和其实现子类RedCircleGreenCircle**

public interface DrawAPI {
    public void drawCircle(int radius,int x,int y);
}
class GreenCircle implements DrawAPI{

    @Override
    public void drawCircle(int radius, int x, int y) {
        System.out.println("正在绘制半径为"+radius+"坐标为:x="+x+" y="+y+"绿色的圆");
    }
}
class RedCircle implements DrawAPI{

    @Override
    public void drawCircle(int radius, int x, int y) {
        System.out.println("正在绘制半径为"+radius+"坐标为:x="+x+" y="+y+"红色的圆");
    }
}

使用 DrawAPI 接口创建抽象类 *Shape 和其实现子类Circle*

public abstract class Shape {
    protected DrawAPI drawAPI;

    protected Shape(DrawAPI drawAPI){
        this.drawAPI = drawAPI;
    }
    public abstract void draw();
}
class Circle extends Shape{
    private int x,y,radius;

    protected Circle(int radius,int x,int y,DrawAPI drawAPI) {
        super(drawAPI);
        this.x = x;
        this.y = y;
        this.radius = radius;
    }

    @Override
    public void draw() {
        drawAPI.drawCircle(radius,x,y);
    }
}

Client使用 ShapeDrawAPI 类画出不同颜色的圆

public class Client {
    public static void main(String[] args) {
        new Circle(10,10,10,new GreenCircle()).draw();
        new Circle(10,10,10,new RedCircle()).draw();
    }
}

总结

23种设计模式——结构型模式(上半部分)_第4张图片

优点

  • 1、分离抽象接口及其实现部分。提高了比继承更好的解决方案。
  • 2、桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。
  • 3、实现细节对客户透明,可以对用户隐藏实现细节。

缺点

  • 1、桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
  • 2、桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。






过滤器模式(Filter、Criteria Pattern)

过滤器模式(Filter Pattern)或标准模式(Criteria Pattern)是一种设计模式,这种模式允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来。这种类型的设计模式属于结构型模式,它结合多个标准来获得单一标准。

简介

优点:

  • 简单,解耦,使用方便。

缺点:

  • 查了资料发现这货竟然没什么缺点?算了,日后补充吧

这绝对是我见过的最没存在感的设计模式了。

应用案例:

  • Spring MVC 中的过滤器,用来过滤掉某些请求。

实现

我们将创建一个 Person 对象、Criteria 接口和实现了该接口的实体类,来过滤 Person 对象的列表。CLient类使用 Criteria 对象,基于各种标准和它们的结合来过滤 Person 对象的列表。

23种设计模式——结构型模式(上半部分)_第5张图片

创建一个类Person,在该类上应用标准。

public class Person {
    private String name;
    private String gender;
    private String maritalStatus;

    public Person(String name, String gender, String maritalStatus) {
        this.name = name;
        this.gender = gender;
        this.maritalStatus = maritalStatus;
    }
    public String getName() {
        return name;
    }
    public String getGender() {
        return gender;
    }
    public String getMaritalStatus() {
        return maritalStatus;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", maritalStatus='" + maritalStatus + '\'' +
                '}';
    }
}

为标准创建一个接口Criteria 和其实现子类FemaleCriteria MaleCriteria SingleCriteria

public interface Criteria {
    public List<Person> meetCriteria(List<Person> personList);
}

class FemaleCriteria implements Criteria{
    @Override
    public List<Person> meetCriteria(List<Person> personList) {
        List<Person> femalePersons =new ArrayList<>();
        for (Person person:personList) {
            if (person.getGender().equalsIgnoreCase("female")){
                femalePersons.add(person);
            }
        }
        return femalePersons;
    }
}

class MaleCriteria implements Criteria{
    @Override
    public List<Person> meetCriteria(List<Person> personList) {
        List<Person> malePersons =new ArrayList<>();
        for (Person person:personList) {
            if (person.getGender().equalsIgnoreCase("male")){
                malePersons.add(person);
            }
        }
        return malePersons;
    }
}

class SingleCriteria implements Criteria{
    @Override
    public List<Person> meetCriteria(List<Person> personList) {
        List<Person> singlePersons =new ArrayList<>();
        for (Person person:personList) {
            if (person.getMaritalStatus().equalsIgnoreCase("single")){
                singlePersons.add(person);
            }
        }
        return singlePersons;
    }
}

实现Criteria并组合标准类AndCriteria OrCriteria

public class AndCriteria implements Criteria{
    private Criteria criteria;
    private Criteria otherCriteria;

    public AndCriteria(Criteria criteria, Criteria otherCriteria) {
        this.criteria = criteria;
        this.otherCriteria = otherCriteria;
    }
    @Override
    public List<Person> meetCriteria(List<Person> personList) {
        List<Person> firstCriteriaPersons = criteria.meetCriteria(personList);
        List<Person> secondCriteriaPersons = otherCriteria.meetCriteria(firstCriteriaPersons);
        return secondCriteriaPersons;
    }
}

class OrCriteria implements Criteria{
    private Criteria criteria;
    private Criteria otherCriteria;
    public OrCriteria(Criteria criteria, Criteria otherCriteria) {
        this.criteria = criteria;
        this.otherCriteria = otherCriteria;
    }
    @Override
    public List<Person> meetCriteria(List<Person> personList) {
        List<Person> firstCriteriaPersons = criteria.meetCriteria(personList);
        List<Person> secondCriteriaPersons = otherCriteria.meetCriteria(firstCriteriaPersons);
      for(Person person:firstCriteriaPersons){
          if(!secondCriteriaPersons.contains(person)){
              secondCriteriaPersons.add(person);
          }
      }
        return secondCriteriaPersons;
    }
}

Client端调用Criteria

public class Client {
    public static void main(String[] args) {
        Person  person1= new Person("mike", "male", "single");
        Person  person2= new Person("mary", "female", "no");
        Person  person3= new Person("scary", "female", "single");
        Person  person4= new Person("tom", "male", "no");
        Person  person5= new Person("amy", "female", "single");

        List<Person> personList = new ArrayList<>();
        personList.add(person1);
        personList.add(person2);
        personList.add(person3);
        personList.add(person4);
        personList.add(person5);

        FemaleCriteria femaleCriteria = new FemaleCriteria();
        MaleCriteria maleCriteria = new MaleCriteria();
        SingleCriteria singleCriteria = new SingleCriteria();
        AndCriteria femaleAndSingle = new AndCriteria(femaleCriteria, singleCriteria);
        OrCriteria maleOrSingle = new OrCriteria(maleCriteria, singleCriteria);

        List<Person> personList1 = femaleAndSingle.meetCriteria(personList);
        List<Person> personList2 = maleOrSingle.meetCriteria(personList);

       show(personList1);
        System.out.println("================================");
        show(personList2);
    }
    static void show(List<Person> personList){
        for (Person person:personList){
            System.out.println(person);
        }
    }
}






组合模式(Composite Pattern)

组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于==结构型模式,==它创建了对象组的树形结构。

这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。

介绍

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

**主要解决:**它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。

何时使用:

  1. 您想表示对象的部分-整体层次结构(树形结构)。
  2. 您希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

**如何解决:**树枝和叶子实现统一接口,树枝内部组合该接口。

**关键代码:**树枝内部组合该接口,并且含有内部属性 List,里面放 Component。

应用实例:

  1. 算术表达式包括操作数、操作符和另一个操作数,其中,另一个操作数也可以是操作数、操作符和另一个操作数。
  2. 在 JAVA AWT 和 SWING 中,对于 Button 和 Checkbox 是树叶,Container 是树枝。

优点: 1、高层模块调用简单。 2、节点自由增加。

**缺点:**在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。

**使用场景:**部分、整体场景,如树形菜单,文件、文件夹的管理。

实现

我们有一个类Employee,该类被当作组合模型类。Client 类使用 Employee 类来添加部门层次结构,并打印所有员工

23种设计模式——结构型模式(上半部分)_第6张图片

被当作组合模型类Employee

public class Employee {
    private String name;
    private String dept;
    private int salary;
    private List<Employee> subordinates = new ArrayList<>();

    public Employee(String name, String dept, int salary) {
        this.name = name;
        this.dept = dept;
        this.salary = salary;
    }
    public void add(Employee employee){
        subordinates.add(employee);
    }
    public void remove(Employee employee){
        subordinates.remove(employee);
    }
    public List<Employee> getSubordinates(){
        return this.subordinates;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", subordinates=" + subordinates +
                '}';
    }
}

Client组合Employee

public class Client {
    public static void main(String[] args) {
        Employee CEO = new Employee("CEO", "CBD", 10000);

        Employee CFO = new Employee("CFO", "CFO", 20000);

        Employee Mack = new Employee("Mack", "Mack", 15000);

        Employee employee1 = new Employee("employee1", "employee1", 1000);
        Employee employee2 = new Employee("employee2", "employee2", 1000);
        Employee employee3 = new Employee("employee3", "employee3", 1000);
        Employee employee4 = new Employee("employee4", "employee4", 1000);

        CEO.add(CFO);
        CEO.add(Mack);

        Mack.add(employee1);
        Mack.add(employee2);

        CFO.add(employee3);
        CFO.add(employee4);

        List<Employee> subordinates = CEO.getSubordinates();
        System.out.println(CEO);
        for (Employee employee:subordinates){
            System.out.println(employee);
            for(Employee employee5:employee.getSubordinates()){
                System.out.println(employee5);
            }
            System.out.println("----------------");
        }
    }

你可能感兴趣的:(java设计模式,设计模式)