liuyulin
发布于 2024-02-02 / 37 阅读
0
0

设计模式

设计模式

设计模式代表了最佳的实践

设计模式的类型

23种设计模式可以分为三大类:创建型模式,结构型模式和行为型模式。还有一种J2EE模式

序号

模式&描述

包括

1

创建型模式提供了一种在创建对象的同时,隐藏创建逻辑的方式,而不是直接new运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。

- 工厂模式- 抽象工厂模式- 单例模式- 建造者模式- 原型模式

2

结构型模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式

- 适配器模式- 桥接模式- 过滤器模式- 组合模式- 装饰器模式- 外观模式- 享元模式- 代理模式

3

行为型模式这些模式特别关注对象之间的通信

- 责任链模式- 命令模式- 解释器模式- 迭代器模式- 中介者模式- 备忘录模式- 观察者模式- 状态模式- 空对象模式- 策略模式- 模板模式- 访问者模式

4

J2EE模式这些模式特变关注表示层

- MVC模式- 业务代表模式- 组合实体模式- 数据访问对象模式- 前端控制器模式- 拦截过滤器模式- 服务定位模式- 传输对象模式

设计模式六大原则

  1. 开闭原则:对扩展开放,对修改关闭。

  2. 里氏代换原则

  3. 依赖倒转原则:针对接口编程,依赖于抽象而不依赖于具体。

  4. 接口隔离原则:使用多个隔离的接口比使用单个接口要好,它还有另外一个意思是:降低类之间的耦合度。

  5. 迪米特法则(最少指导原则):一个实体应当尽量少地于其他实体之间发生相互作用,使得系统功能模块相互独立。

  6. 合成复用原则:尽量使用合成/聚合的方式,而不是使用继承。

工厂模式

工厂模式是Java种最常用的设计模式之一,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式种,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

介绍

意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟子类进行。

主要解决:我们明确地计划不同条件下创建不同实例时。

何时使用:让其子类实现工厂接口,返回的也是一个抽线的产品。

关键代码:创建过程在其子类执行。

优点:

  1. 一个调用者想创建一个对象,只要知道其名称就可以了。

  2. 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。

  3. 屏蔽产品的具体实现,调用者只关心产品的接口。

缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统种类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂模式。有一点需要注意的地方就是,复杂对象适合使用工厂模式,而简单对象,特别是只需要用过new就可以完成创建的对象,无需使用工厂模式,如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

实现

public interface Shape {
    void draw();
}
public class Circle implements Shape{
    @Override
    public void draw() {
        System.out.println("Inside Circle::draw() method.");
    }
}
public class Square implements Shape{
    @Override
    public void draw() {
        System.out.println("Inside Square::draw() method.");
    }
}
public class ShapeFactory {
    public Shape getShape(String shapeType){
        if (shapeType == null){
            return null;
        }

        if (shapeType.equalsIgnoreCase("CIRCLE")){
            return new Circle();
        }else if (shapeType.equalsIgnoreCase("SQUARE")){
            return new Square();
        }

        return null;
    }
}
public class FactoryPatternDemo {
    public static void main(String[] args) {
        ShapeFactory shapeFactory = new ShapeFactory();

        Shape shape1 = shapeFactory.getShape("circle");
        shape1.draw();

        Shape shape2 = shapeFactory.getShape("SQUARE");
        shape2.draw();
    }
}

抽象工厂模式

抽象工厂就是为工厂再封装一层工厂抽象工厂模式是围绕一个超级工厂创建其他工厂,超级工厂又称为工厂的工厂,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在抽象工厂模式中,接口时负责创建一个相关对象的工厂,不需要显示指定他们的类,每个生成的工厂都能按照工厂模式提供对象。

介绍

意图:提供一个创建一系列相关或相互依赖的接口,而无需指定他们具体的类

主要解决:主要解决接口选择的问题

何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品

如何解决:在一个产品族里面定义多个产品

关键代码:在一个工厂里聚合多个同类产品

优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象

缺点:产品族拓展非常困难,要增加一个系列的某一产品,既要在冲向的Creator里加代码,又要在具体的里面加代码

注意事项:产品族难扩展,产品等级易扩展

实现

Shape工厂继续沿用工厂模式中编写的代码ShapeFactory:

public class ShapeFactory extends AbstractFactory {
    @Override
    public Color getColor(String color) {
        return null;
    }

    @Override
    public Shape getShape(String shapeType){
        if (shapeType == null){
            return null;
        }

        if (shapeType.equalsIgnoreCase("CIRCLE")){
            return new Circle();
        }else if (shapeType.equalsIgnoreCase("SQUARE")){
            return new Square();
        }

        return null;
    }
}

Color接口:

public interface Color {
    void fill();
}

Red类:

public class Red implements Color {
    @Override
    public void fill() {
        System.out.println("Inside Red::fill() method.");
    }
}

Blue类:

public class Blue implements Color {
    @Override
    public void fill() {
        System.out.println("Inside Blue::fill() method.");
    }
}

ColorFactory:

public class ColorFactory extends AbstractFactory{
    @Override
    public Color getColor(String color) {
        if (color == null){
            return null;
        }

        if (color.equalsIgnoreCase("RED")){
            return new Red();
        } else if (color.equalsIgnoreCase("BLUE")) {
            return new Blue();
        }else {
            return null;
        }
    }

    @Override
    public Shape getShape(String shape) {
        return null;
    }
}

AbstractFactory:

/**
 * 为Color和Shape对象创建抽象类来获取工厂
 */
public abstract class AbstractFactory {
    public abstract Color getColor(String color);
    public abstract Shape getShape(String shape);
}

FactoryProducer:

/**
 * 创建一个工厂创造器/生成器 通过传递形状或颜色来获取工厂
 */
public class FactoryProducer {
    public static AbstractFactory getFactory(String choice){
        if (choice.equalsIgnoreCase("SHAPE")){
            return new ShapeFactory();
        } else if (choice.equalsIgnoreCase("COLOR")) {
            return new ColorFactory();
        }else {
            return null;
        }
    }
}

Demo:

public class AbstractFactoryDemo {
    public static void main(String[] args) {
        //获取形状工厂 用于创建形状对象
        AbstractFactory shapeFactory = FactoryProducer.getFactory("SHAPE");
        Shape shape1 = shapeFactory.getShape("circle");
        shape1.draw();
        Shape shape2 = shapeFactory.getShape("Square");
        shape2.draw();

        AbstractFactory colorFactory = FactoryProducer.getFactory("color");
        Color color1 = colorFactory.getColor("ReD");
        color1.fill();
        Color color2 = colorFactory.getColor("blUe");
        color2.fill();
    }
}

单例模式

单例模式一个类只有一个实例单例模式时java中最简单的设计模式之一,它提供了一种创造对象的最佳方法。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建,提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。注意:

  1. 单例类只能有一个实例

  2. 单例类必须自己创建自己的唯一实例

  3. 单例类必须给其他对象提供这一实例

介绍

意图:保证一个类仅有一个实例,并且提供一个访问它的全局访问点

主要解决:一个全局使用的类频繁地创建和销毁

何时使用:想控制实例数目,节省系统资源的时候

如何解决:判断系统是否已有这个单例,如果有则返回,如果没有则创建

关键代码:构造函数是私有的优点

  1. 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例

  2. 避免对资源的多重占用

注意事项:getInstance()方法中需要使用同步锁synchronized(Singleton.class)防止多线程同时进入造成instance被多次实例化

实现

SingleObject:

public class SingleObject {

    private static SingleObject instance = new SingleObject();

    //构造方法为private,这样类就没办法被实例化
    private SingleObject() {
    }

    public static SingleObject getInstance(){
        return instance;
    }

    public void showMsg(){
        System.out.println("Singleton Pattern");
    }
}

SingletonDemo:

public class SingletonDemo {
    public static void main(String[] args) {
        SingleObject singleObject = SingleObject.getInstance();
        singleObject.showMsg();
    }
}

单例模式的几种实现方式

懒汉式,线程不安全

这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程,因为没有加锁synchroized,所以严格意义上它并不算单例模式。这种方式Lazy loading很明显,不要求线程安全,在多线程不能正常工作。

是否Lazy初始化:是

是否多线程安全:否

实现难度:易

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
  
    public static Singleton getInstance() {  
        if (instance == null) {  
            instance = new Singleton();  
        }  
        return instance;  
    }  
}

懒汉式,线程安全

这种方式具备很好的lazyloading,能够在多线程中很好的工作,但是效率很低,99%情况下不需要同步

是否Lazy初始化:是

是否多线程安全:是

实现难度:易

优点:第一次调用才初始化,避免内存浪费

缺点:必须加锁synchronized才能保证单例,但加锁会影响效率。getInstance()的性能对应用程序不是很关键(该方法使用不太频繁)

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
        if (instance == null) {  
            instance = new Singleton();  
        }  
        return instance;  
    }  
}

饿汉式

这种方式比较常用,但是容易产生垃圾对象。它是基于classloader机制避免了多线程的同步问题,不过,instance在类装载时就会实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法,但是也不确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到lazy loading的效果。

是否Lazy初始化:否

是否多线程安全:是

实现难度:易

public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return instance;  
    }  
}

双检锁/双重校验锁(DCL)

这种方式采用双锁机制,安全且在多线程情况下能保持高性能,getInstance()的性能对应用程序很关键

是否Lazy初始化:是

是否多线程安全:是

实现难度:较复杂

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
            if (singleton == null) {  
                singleton = new Singleton();  
            }  
        }  
    }  
    return singleton;  
    }  
}

登记式/静态内部类

这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟第 3 种方式不同的是:第 3 种方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比第 3 种方式就显得很合理。

是否Lazy初始化:是

是否多线程安全:是

实现难度:一般

public class Singleton {  
    private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
        return SingletonHolder.INSTANCE;  
    }  
}

枚举

这种方式还没有被广泛使用,但这是实现单例的最佳方式,它简洁,自动支持序列化机制,绝对防止多次实例化。不仅能避免多线程问题,还能自动支持序列化机制,防止反序列化重新创建对象,绝对防止多次实例化。

是否Lazy初始化:否

是否多线程安全:是

实现难度:易

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}

总结

一般情况下,不建议使用第1种和第2种懒汉方式,建议使用第3种饿汉方式,只有在要明确实现lazy loading效果时,才会使用第5种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第6种枚举方式。如果有其他特殊的需求,可以考虑第四种双检锁方式。

建造者模式

建造者模式是使用多个简单的对象一步一步构建成一个复杂的对象,它提供了一种创建对象的最佳方式。一个Builder类会一步一步构造最终的对象,该Builder类是独立于其他对象的。

介绍

意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示

主要解决:主要解决在软件系统中,有时候面临着一个复杂对象的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将他们组合在一起的算法却相对稳定

何时使用:一些基本部件不会变,而其组合经常变化的时候

如何解决:将变与不变分离开

关键代码:建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系

优点:建造者独立,易扩展;便于控制细节风险

缺点:产品必须有共同点,范围有限制;如内部变化复杂,会有很多的建造类使用场景:需要生成的对象具有复杂的内部结构;需要生成的对象内部属性本身相互依赖

注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序

实现

接口类

public interface Item {
   public String name();
   public Packing packing();
   public float price();    
}
public interface Packing {
   public String pack();
}

实现Packing接口的实体类,直接定义为实体类,是因为这狼类不会经常变化

public class Wrapper implements Packing {
 
   @Override
   public String pack() {
      return "Wrapper";
   }
}
public class Bottle implements Packing {
 
   @Override
   public String pack() {
      return "Bottle";
   }
}

实现Item的抽象类

public abstract class Burger implements Item {
 
   @Override
   public Packing packing() {
      return new Wrapper();
   }
 
   @Override
   public abstract float price();
}
public abstract class ColdDrink implements Item {
 
    @Override
    public Packing packing() {
       return new Bottle();
    }
 
    @Override
    public abstract float price();
}

创建实体类

public class VegBurger extends Burger {
 
   @Override
   public float price() {
      return 25.0f;
   }
 
   @Override
   public String name() {
      return "Veg Burger";
   }
}
public class ChickenBurger extends Burger {
 
   @Override
   public float price() {
      return 50.5f;
   }
 
   @Override
   public String name() {
      return "Chicken Burger";
   }
}
public class Coke extends ColdDrink {
 
   @Override
   public float price() {
      return 30.0f;
   }
 
   @Override
   public String name() {
      return "Coke";
   }
}
public class Pepsi extends ColdDrink {
 
   @Override
   public float price() {
      return 35.0f;
   }
 
   @Override
   public String name() {
      return "Pepsi";
   }
}

创建Meal类,整合上面实体类

public class Meal {
   private List<Item> items = new ArrayList<Item>();    
 
   public void addItem(Item item){
      items.add(item);
   }
 
   public float getCost(){
      float cost = 0.0f;
      for (Item item : items) {
         cost += item.price();
      }        
      return cost;
   }
 
   public void showItems(){
      for (Item item : items) {
         System.out.print("Item : "+item.name());
         System.out.print(", Packing : "+item.packing().pack());
         System.out.println(", Price : "+item.price());
      }        
   }

创建Builder,负责创建对象

public class MealBuilder {
 
   public Meal prepareVegMeal (){
      Meal meal = new Meal();
      meal.addItem(new VegBurger());
      meal.addItem(new Coke());
      return meal;
   }   
 
   public Meal prepareNonVegMeal (){
      Meal meal = new Meal();
      meal.addItem(new ChickenBurger());
      meal.addItem(new Pepsi());
      return meal;
   }
}

Demo

public class BuilderPatternDemo {
   public static void main(String[] args) {
      MealBuilder mealBuilder = new MealBuilder();
 
      Meal vegMeal = mealBuilder.prepareVegMeal();
      System.out.println("Veg Meal");
      vegMeal.showItems();
      System.out.println("Total Cost: " +vegMeal.getCost());
 
      Meal nonVegMeal = mealBuilder.prepareNonVegMeal();
      System.out.println("\n\nNon-Veg Meal");
      nonVegMeal.showItems();
      System.out.println("Total Cost: " +nonVegMeal.getCost());
   }
}

原型模式

原型模式用于创建重复的对象,同时又能保证性能。这种模式就是实现了一个原型接口,该接口用于创建当前对象的克隆,当直接创建对象的代价比较大时,则采用这种模式。

介绍

意图:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象

主要解决:在运行期建立和删除原型

何时使用

  1. 当一个系统应该独立于它的产品创建,构造和表示时

  2. 当要实例化的类时在运行时刻指定时

  3. 为了避免创建一个与产品类层次平行的工厂类层次时

  4. 当一个类的实例只能有几个不同状态组合中的一种时,建立相应数目的原型并克隆他们可能比每次用合适的状态手工实例化该类更方便一些

如何解决:利用已有的一个原型对象,快速地生成和原型对象一样的实例

关键代码

  1. 实现克隆操作,在java中实现Cloneable接口,重写clone()

  2. 原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些易变类拥有稳定的接口

优点:性能提高;逃避构造函数的约束

缺点

  1. 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别是当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候

  2. 必须实现Cloneable接口

使用场景

  1. 资源优化场景

  2. 类初始化需要消耗非常多的资源,这个资源包括数据、硬件资源等

  3. 性能和安全要求的场景

  4. 通过new产生一个对象需要非常繁琐的数据准备和访问权限,则可以使用原型模式

  5. 一个对象多个修改者的场景

  6. 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用

  7. 在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者

注意事项:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的,浅拷贝实现Cloneable,深拷贝是通过实现Serializable读取二进制流

实现

抽象类

public abstract class Employee implements Cloneable{
    private String name;
    protected String level;

    abstract void work();

    public String getName() {
        return name;
    }

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

    public String getLevel() {
        return level;
    }

    public void setLevel(String level) {
        this.level = level;
    }

    @Override
    protected Object clone(){
        Object clone = null;
        try {
            clone = super.clone();
        }catch (CloneNotSupportedException e){
            e.printStackTrace();
        }
        return clone;
    }
}

实体类

public class Engineer extends Employee{

    public Engineer() {
        level = "P";
    }

    @Override
    void work() {
        System.out.println("Engineers work 12 hours a day");
    }
}
public class Manager extends Employee{

    public Manager() {
        level = "M";
    }

    @Override
    void work() {
        System.out.println("Managers works 8 hours a day");
    }
}

模拟缓存

public class EmployeeCache {

    private static Hashtable<String, Employee> employeeMap = new Hashtable<>();

    public static Employee getEmployee(String name){
        Employee cachedEmployee = employeeMap.get(name);
        return (Employee) cachedEmployee.clone();
    }

    //数据一般来源于缓存,这里用假数据模拟
    public static void loadCache(){
        Engineer e1 = new Engineer();
        e1.setName("liuyulin10");
        employeeMap.put(e1.getName(),e1);

        Manager m1 = new Manager();
        m1.setName("zhangxue5");
        employeeMap.put(m1.getName(),m1);
    }
}

Demo

public class PrototypeDemo {

    public static void main(String[] args) {
        EmployeeCache.loadCache();

        Employee employee1 = EmployeeCache.getEmployee("liuyulin10");
        System.out.println("Worker:" + employee1.getName() + " Level:" + employee1.getLevel());

        Employee employee2 = EmployeeCache.getEmployee("zhangxue5");
        System.out.println("Worker:" + employee2.getName() + " Level:" + employee2.getLevel());
    }
}

适配器模式

适配器模式是作为两个不兼容的接口之间的桥梁,这种模式涉及到一个单一的类,该类负责加入独立或不兼容的接口功能。

介绍

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

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

何时使用

  1. 系统需要使用现有的类,而此类的接口不符合系统的需要

  2. 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些类不一定有一致的接口

  3. 通过接口转换,将一个类插入另一个类系中

如何解决:继承或依赖

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

优点

  1. 可以让任何两个没有关联的类一起运行

  2. 提高类的复用

  3. 增加了类的透明度

  4. 灵活性好

缺点

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

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

使用场景:有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题

实现

接口

public interface MediaPlayer {
    void play(String audioType,String fileName);
}
public interface AdvancedMediaPlayer {
    public void playVlc(String fileName);
    public void playMp4(String fileName);
}

实现AdvancedMediaPlayer的实体类

public class Mp4Player implements AdvancedMediaPlayer {
    @Override
    public void playVlc(String fileName) {

    }

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

    @Override
    public void playMp4(String fileName) {

    }
}

实现MediaPlayer接口的实体类,通过添加AdvancedMediaPlayer接口在其中,添加两个方法

public class MediaAdapter implements MediaPlayer{

    AdvancedMediaPlayer advancedMediaPlayer;

    public MediaAdapter(String audioType) {
        if (audioType.equalsIgnoreCase("vlc")){
            advancedMediaPlayer = new VlcPlayer();
        }else if (audioType.equalsIgnoreCase("mp4")){
            advancedMediaPlayer = new Mp4Player();
        }
    }

    @Override
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("vlc")){
            advancedMediaPlayer.playVlc(fileName);
        }else if (audioType.equalsIgnoreCase("mp4")){
            advancedMediaPlayer.playMp4(fileName);
        }
    }
}

创建实现了MediaPlayer接口的实体类

public class AudioPlayer implements MediaPlayer{

    MediaAdapter mediaAdapter;

    @Override
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("mp3")){
            System.out.println("Playing mp3 file:"+fileName);
        }else if (audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")){
            mediaAdapter = new MediaAdapter(audioType);
            mediaAdapter.play(audioType,fileName);
        }else {
            System.out.println("Invalid media type:"+audioType);
        }
    }
}

Demo

public class AdapterDemo {
    public static void main(String[] args) {
        AudioPlayer audioPlayer = new AudioPlayer();

        audioPlayer.play("mp3", "ABC.mp3");
        audioPlayer.play("mp4", "Teacher Cang.mp4");
        audioPlayer.play("vlc", "what is vlc.vlc");
        audioPlayer.play("com", "www.baidu.com");
    }
}

桥接模式

桥接模式是用于把抽象化与实现化解耦,使得二者可以独立变化,通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类,这两种类型的类可被结构化改变而不相互影响。

介绍

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

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

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

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

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

优点

  1. 抽象和实现的分离

  2. 优秀的扩展能力

  3. 实现细节对客户透明

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

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

实现

接口

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

实现DrawAPI的类

public class GreenCircle implements DrawAPI{

    @Override
    public void drawCircle(int radius, int x, int y) {
        System.out.println("[Green] circle: [radius]:"+radius+" [x]:"+x+" [y]:"+y);
    }
}
public class RedCircle implements DrawAPI{
    @Override
    public void drawCircle(int radius, int x, int y) {
        System.out.println("[RED] circle: [radius]:"+radius+" [x]:"+x+" [y]:"+y);
    }
}

抽象类

public abstract class Shape {
    protected DrawAPI drawAPI;

    public Shape(DrawAPI drawAPI) {
        this.drawAPI = drawAPI;
    }

    public abstract void draw();
}

实体类

public class Circle extends Shape{
    private int radius,x,y;

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

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

Demo

public class BridgeDemo {
    public static void main(String[] args) {
        Shape circle1 = new Circle(1,2,3,new RedCircle());
        Circle circle2 = new Circle(4, 5, 6, new GreenCircle());

        circle1.draw();
        circle2.draw();
    }
}

过滤器模式

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

实现

创建一个类,后续在该类上应用标准

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;
    }
}

为标准创建一个接口

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

创建实现了接口的实体类,用于过滤

public class CriteriaMale implements Criteria{

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

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

    @Override
    public List<Person> meetCriteria(List<Person> persons) {
        List<Person> singlePersons = new ArrayList<Person>();
        for (Person person : persons) {
            if (person.getMaritalStatus().equalsIgnoreCase("SINGLE")) {
                singlePersons.add(person);
            }
        }
        return singlePersons;
    }
}

复合 标准

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> persons) {
        //第一轮过滤,过滤符合符合标准1的数据
        List<Person> firstCriteriaPersons = criteria.meetCriteria(persons);
        //第二轮过滤,在符合标准1的注解中再过滤出符合标准2的数据,达到AND过滤的目的
        return otherCriteria.meetCriteria(firstCriteriaPersons);
    }
}
public 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> persons) {
        //过滤出符合两个标准的数据
        List<Person> firstCriteriaItems = criteria.meetCriteria(persons);
        List<Person> otherCriteriaItems = otherCriteria.meetCriteria(persons);

        //遍历标准2过滤出的数据,如果标准1中不包含,则加入标准1,最后返回标准1表
        for (Person person : otherCriteriaItems) {
            if(!firstCriteriaItems.contains(person)){
                firstCriteriaItems.add(person);
            }
        }
        return firstCriteriaItems;
    }
}

Demo

public class CriteriaPatternDemo {
    public static void main(String[] args) {
        List<Person> persons = new ArrayList<Person>();

        persons.add(new Person("Robert","Male", "Single"));
        persons.add(new Person("John","Male", "Married"));
        persons.add(new Person("Laura","Female", "Married"));
        persons.add(new Person("Diana","Female", "Single"));
        persons.add(new Person("Mike","Male", "Single"));
        persons.add(new Person("Bobby","Male", "Single"));

        Criteria male = new CriteriaMale();
        Criteria female = new CriteriaFemale();
        Criteria single = new CriteriaSingle();
        Criteria singleMale = new AndCriteria(single, male);
        Criteria singleOrFemale = new OrCriteria(single, female);

        System.out.println("Males: ");
        printPersons(male.meetCriteria(persons));

        System.out.println("\nFemales: ");
        printPersons(female.meetCriteria(persons));

        System.out.println("\nSingle Males: ");
        printPersons(singleMale.meetCriteria(persons));

        System.out.println("\nSingle Or Females: ");
        printPersons(singleOrFemale.meetCriteria(persons));
    }

    public static void printPersons(List<Person> persons){
        for (Person person : persons) {
            System.out.println("Person : [ Name : " + person.getName()
                    +", Gender : " + person.getGender()
                    +", Marital Status : " + person.getMaritalStatus()
                    +" ]");
        }
    }
}

组合模式

组合模式又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次,属于结构型模式,它创建了对象组的树形结构。这种模式创建了一个包含自己对象组的类,该类提供了修改相同对象组的方式。

介绍

意图:将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合的使用具有一致性。主要解决:它在我们树形结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦何时使用:

  1. 想表示对象的部分-整体层次结构(树形结构)

  2. 希望用户忽略组合对象与单个对象的不同,用户将同一地使用组合结构中的所有对象

如何解决:树枝和叶子实现统一接口,树枝内部组合该接口关键代码:树枝内部组合该接口,并且含有内部属性List,里面放Component优点:高层模块调用简单;节点自由增加缺点:在使用组合模式时,其叶子和树枝生命都是实现类,而不是接口,违反了依赖倒置原则使用场景:部分、整体场景,如树形菜单,文件、文件夹的管理注意事项:定义时为具体类

实现

实体类

public class Employee {
    private String name;
    private String dept;
    private int salary;
    private List<Employee> subordinates;

    public Employee(String name, String dept, int salary) {
        this.name = name;
        this.dept = dept;
        this.salary = salary;
        this.subordinates = new ArrayList<>();
    }

    public void addSubordinate(Employee subordinate){
        subordinates.add(subordinate);
    }

    public void removeSubordinate(Employee subordinate){
        subordinates.remove(subordinate);
    }

    public List<Employee> getSubordinates() {
        return subordinates;
    }

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

Demo

public class CompositeDemo {
    public static void main(String[] args) {
        Employee CEO = new Employee("John","CEO", 30000);

        Employee headSales = new Employee("Robert","Head Sales", 20000);

        Employee headMarketing = new Employee("Michel","Head Marketing", 20000);

        Employee clerk1 = new Employee("Laura","Marketing", 10000);
        Employee clerk2 = new Employee("Bob","Marketing", 10000);

        Employee salesExecutive1 = new Employee("Richard","Sales", 10000);
        Employee salesExecutive2 = new Employee("Rob","Sales", 10000);

        CEO.addSubordinate(headSales);
        CEO.addSubordinate(headMarketing);

        headSales.addSubordinate(salesExecutive1);
        headSales.addSubordinate(salesExecutive2);

        headMarketing.addSubordinate(clerk1);
        headMarketing.addSubordinate(clerk2);

        //打印该组织的所有员工
        System.out.println(CEO);
        for (Employee headEmployee : CEO.getSubordinates()) {
            System.out.println(headEmployee);
            for (Employee employee : headEmployee.getSubordinates()) {
                System.out.println(employee);
            }
        }
    }
}

从这里开始认真学习设计模式,之前学的不认真的部分要复习一下

装饰器模式

装饰器模式允许向一个现有的对象添加新的功能同时又不改变其结构,属于结构型模式,是作为现有的类的一个包装。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

介绍

意图:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式相比子类更灵活主要解决:一般的,为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀何时使用:在不想增加更多子类的情况下扩展类如何解决:将具体功能职责划分,同时继承装饰者模式关键代码:

  1. Component类充当抽象角色,不应该具体实现

  2. 装饰类引用和继承Component类,具体扩展类重写父类方法

优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能缺点:多层装饰比较复杂使用场景:扩展一个类的功能;动态增加功能,动态撤销注意事项:可代替继承

实现

Shape接口

public interface Shape {
    void draw();
}

实体类

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

抽象装饰器

public abstract class ShapeDecorator implements Shape {

    protected Shape decoratoredShape;

    public ShapeDecorator(Shape decoratoredShape){
        this.decoratoredShape = decoratoredShape;
    }

    @Override
    public void draw() {
        decoratoredShape.draw();
    }
}

装饰器实体类

public class RedShapeDecorator extends ShapeDecorator{

    public RedShapeDecorator(Shape decoratoredShape) {
        super(decoratoredShape);
    }

    @Override
    public void draw(){
        decoratoredShape.draw();
        setRedBoarder(decoratoredShape);
    }

    public void setRedBoarder(Shape decoratoredShape){
        System.out.println("Border colot:Red");
    }
}

Demo

public class DecoratorPatternDemo {

    public static void main(String[] args) {
        Shape circle = new Circle();
        ShapeDecorator redCircle = new RedShapeDecorator(new Circle());
        ShapeDecorator redRectangle = new RedShapeDecorator(new Rectangle());

        circle.draw();
        redCircle.draw();
        redRectangle.draw();
    }
}

外观模式

介绍

外观模式隐藏系统的复杂性,并向客户端提供了一个接口,属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性

意图:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

主要解决:降低访问复杂系统的内部子系统的复杂度,简化客户端之间的接口。

何时使用

  • 客户端不需要知道系统内部的复杂联系,整个系统只需提供一个"接待员"即可

  • 定义系统的入口

如何解决:客户端不与系统耦合,外观类与系统耦合

关键代码:在客户端和复杂系统之间再加一层,这一层将调用顺序、依赖关系等处理好。

应用实例:controller,service,serviceImpl三层开发模式

优点

  • 减少系统的相互依赖

  • 提高灵活性

  • 提高安全性

缺点

不符合开闭原则,如果要改东西很麻烦,继承重写都不合适

实现

这里就略了,三层开发模式就是使用的外观模式,每天都在用

享元模式

享元模式主要用于减少创建对象的数量,以减少内存占用的和提高性能,属于结构型模式,提供了减少对象数量从而改善应用所需的对象结构的方式。

介绍

意图:运用共享技术有效地支持大量细粒度的对象

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

何时使用

  • 系统中有大量对象

  • 这些对象消耗大量内存

  • 这些对象的状态大部分可以外部化

  • 这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。

  • 系统不依赖于这些对象身份,这些对象是不可分辨的

如何解决:用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象

关键代码:用HashMap存储这些对象

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

优点:大大减少对象的创建,降低系统内存,使效率提高

缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。

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

注意事项:1、注意划分外部状态和内部状态,否则可能会引起线程安全问题。2、这些类必须有一个工厂对象加以控制

实现

实体类

package dp.common;

public class Circle implements Shape {

    private String color;

    private float x;
    private float y;
    private float radius;

    public Circle() {
    }

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

    @Override
    public void draw() {
        System.out.println("【Circle】:\n" +
                "【color】:" + color +
                "【x】:" + x + "【y】:" + y + "【radius】:" + radius);
    }

    public void setColor(String color) {
        this.color = color;
    }

    public void setX(float x) {
        this.x = x;
    }

    public void setY(float y) {
        this.y = y;
    }

    public void setRadius(float radius) {
        this.radius = radius;
    }
}

工厂

package dp.factory;

import dp.common.Circle;
import dp.common.Shape;

import java.util.HashMap;

public class ShapeFactory {

    // 享元工厂
    private static final HashMap<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("Create circle of colot : " + color);
        }

        return circle;
    }
}

Demo

package dp.flyweight;

import dp.common.Circle;
import dp.factory.ShapeFactory;

// 享元模式
public class FltweightDemo {

    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);
    }
}

代理模式

在代理模式中,一个类代表另一个类的功能,属于结构型模式,我们创建具有现有对象的对象,以便向外界提供功能接口。

介绍

意图:为其他对象提供一种代理,以控制对这个对象的访问。

主要解决:在直接访问对象时带来的问题

何时使用:想在访问一个类时做一些控制

如何解决:增加中间层

关键代码:实现与被代理类组合

应用实例:spring aop

优点:职责清晰、高扩展性、智能化

缺点:1、由于在客户端和真是主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

实现

接口

package dp.proxy;

public interface Image {
    void display();
}

实体类

package dp.proxy;

public class RealImage implements Image {

    private String fileName;

    public RealImage(String fileName) {
        this.fileName = fileName;
        loadFromDisk(fileName);
    }

    @Override
    public void display() {
        System.out.println("Displying " + fileName);
    }

    private void loadFromDisk(String fileName) {
        System.out.println("Loading " + fileName);
    }
}

代理类

package dp.proxy;

public class ProxyImage implements Image {
    private RealImage realImage;
    private String fileName;

    public ProxyImage(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public void display() {
        if (realImage == null){
            realImage = new RealImage(fileName);
        }
        realImage.display();
    }
}

Demo

package dp.proxy;

public class ProxyDemo {
    public static void main(String[] args) {
        Image image = new ProxyImage("test_10mb.jpg");

        // 图像将从磁盘加载
        image.display();
        System.out.println("");
        // 图像不需要从磁盘加载
        image.display();
    }
}

责任链模式

责任链模式为请求创建了一个接收者对象的链,这种模式给予请求的类型,对请求的发送者和接收者进行解耦,属于行为型模式。在这种模式中,通常接收者都包含对另一个接收者的引用,如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,以此类推。

介绍

意图:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

主要解决:职责链上的矗立着负责处理请求,客户只需要将请求发送到职责链上即可,无需关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了

何时使用:在处理消息的时候以过滤很多道

如何解决:拦截的类都实现统一的接口

关键代码:Handler里面聚合它自己,在HandlerRequest里判断是否合适,如果没有达到条件则向下传递,向谁传递之前set进去

优点

  • 降低耦合度,它将请求的发送者和接收者解耦

  • 简化了对象,使得对象不需要知道链的结构

  • 增强给对象指派职责的灵活性,通过改变链内的成员或者调动它们的次序,允许动态地新增或删除责任

  • 增加新的请求处理类很方便

缺点

  • 不能保证请求一定被接收

  • 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用

  • 可能不容易观察运行时的特征,有碍于除错

使用场景

  • 有多个对象可以同时处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定

  • 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求

  • 可动态指定一组对象处理请求

实现

抽象类

package dp.chain;

public abstract class AbstractLogger {
    public static int INFO = 1;
    public static int DEBUG = 2;
    public static int ERROR = 3;

    protected int level;

    protected AbstractLogger nextLogger;

    public void setNextLogger(AbstractLogger nextLogger) {
        this.nextLogger = nextLogger;
    }

    public void logMsg(int level, String msg) {
        if (this.level <= level) {
            log(msg);
        }
        if (nextLogger != null) {
            nextLogger.logMsg(level, msg);
        }
    }

    abstract protected void log(String msg);

}

实现类

package dp.chain;

public class ConsoleLogger extends AbstractLogger {

    public ConsoleLogger(int level) {
        this.level = level;
    }

    @Override
    protected void log(String msg) {
        System.out.println("Standard Console::Logger: " + msg);
    }
}
package dp.chain;

public class ErrorLogger extends AbstractLogger{

    public ErrorLogger(int level){
        this.level = level;
    }

    @Override
    protected void log(String msg) {
        System.out.println("Error Console::Logger: " + msg);

    }
}
package dp.chain;

public class FileLogger extends AbstractLogger{

    public FileLogger(int level){
        this.level = level;
    }

    @Override
    protected void log(String msg) {
        System.out.println("File::Logger: " + msg);
    }
}

Demo

package dp.chain;
​
public class ChainDemo {
​
    // 初始化责任链
    private static AbstractLogger getChainOfLoggers(){
        ErrorLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
        FileLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
        ConsoleLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
​
        errorLogger.setNextLogger(fileLogger);
        fileLogger.setNextLogger(consoleLogger);
​
        return errorLogger;
    }
​
    public static void main(String[] args) {
        AbstractLogger logger = getChainOfLoggers();
​
        logger.logMsg(1,"info");
        logger.logMsg(2,"debug");
        logger.logMsg(3,"error");
    }
}


评论