23种设计模式

设计模式的六大原则

1. 单一职责原则(Single Responsibility Principle)

There should never be more than one reason for a class to change.

单一职责原则简称SRP,这句话可以理解为:一个类改变的原因不应该超过一个.

那这个原则有什么用呢,它让类的职责更单一.这样的话,每个类只需要负责自己的那部分,类的复杂度就会降低.如果职责划分得很清楚,那么代码维护起来也更加容易.试想如果所有的功能都放在了一个类中,那么这个类就会变得非常臃肿,而且一旦出现bug,要在所有代码中去寻找;更改某一个地方,可能要改变整个代码的结构,想想都非常可怕.当然一般时候,没有人会去这么写的.

当然,这个原则不仅仅适用于类,对于接口和方法也适用,即一个接口/方法,只负责一件事,这样的话,接口就会变得简单,方法中的代码也会更少,易读,便于维护.

事实上,由于一些其他的因素影响,类的单一职责在项目中是很难保证的.通常,接口和方法的单一职责更容易实现.

单一职责原则的好处

  1. 代码的粒度降低了,类的复杂度降低了.

  2. 可读性提高了,每个类的职责都很明确,可读性自然更好.

  3. 可维护性提高了,可读性提高了,一旦出现 bug ,自然更容易找到他问题所在.

  4. 改动代码所消耗的资源降低了,更改的风险也降低了.

2. 开放封闭原则 (Open Close Principle)

Software entities like classes, modules and functions should be open for extension but closed for modification.

开闭原则简称OCP,这句话可以理解为:类、模块和函数等软件实体应开放以进行扩展,但应关闭以进行修改.

当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现.

用抽象构建架构,用实现扩展细节.

开闭原则是面向对象设计中最基础的设计原则,它指导我们如何建立稳定灵活的系统,开闭原则只定义了对修改关闭,对扩展开放.其实只要遵循其他5个原则,设计出来的软件就是符合开闭原则的.

3. 里氏替换原则 (Liskov Substitution Principle)

Functions that use use pointers or references to base classes must be able to use objects of derived classes without knowing it.

里氏替换原则简称LSP,这句话可以理解为:所有引用基类的地方必须能透明地使用其子类的对象.

里氏替换原则告诉我们,在软件中将一个基类对象替换成其子类对象,程序将不会产生任何错误和异常;反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象.
里氏替换原则是实现开放封闭原则的重要方式之一.由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象.

4. 迪米特原则 (Law of Demeter)

Talk only to your immediate friends and not to strangers

迪米特原则简称LOD,这句话可以理解为:只与你的直接朋友交谈,不跟"陌生人"说话.

其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用.其目的是降低类之间的耦合度,提高模块的相对独立性.

迪米特原则的优点

迪米特原则要求限制软件实体之间通信的宽度和深度,正确使用迪米特原则有以下两个优点:

  1. 降低了类之间的耦合性,提高了模块的相对独立性.
  2. 由于亲和度降低,从而提高了类的可复用性和系统的扩展性.

迪米特原则,也叫最少知道原则,即一个类对自己依赖的类知道的越少越好.也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部,对外除了提供的public方法,不对外泄露任何信息.

该原则的初衷是降低类的耦合,虽然可以避免与非直接的类通信,但是要通信,就必然会通过一个“中介”来发生关系,过分的使用迪米特原则,会产生大量的中介和传递类,导致系统复杂度变大,所以采用迪米特法则时要反复权衡,既要做到结构清晰,又要高内聚低耦合.

5. 接口隔离原则 (Interface Segregation Principle)

Clients should not be forced to depend upon interfaces that they don`t use.
The dependency of one class to another one should depend on the smallest possible.

接口隔离原则简称ISP,这句话可以理解为:

  • 客户端不应该被迫依赖于他们不使用的接口.
  • 一个类对另一个类的依赖性应该取决于尽可能小的值.

以上两个定义的含义是:要为各个类建立他们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖于他的类去调用.

使用多个隔离的接口,比使用单个接口要好,降低接口之间的耦合度与依赖,方便升级和维护方便

6. 依赖倒置原则 (Dependence Inversion Principle)

High level modules should not depend upon low level modules. Both should depend upon abstractions.
Abstractions should not depend upon details. Details should depend upon abstractions.

依赖倒置原则简称DIP,这句话可以理解为:

  • 高级模块不应依赖于低级模块.两者都应该依赖于抽象.
  • 抽象不应依赖于细节.细节应取决于抽象.

通俗来讲高级模块就是调用端,低级模块就是具体实现类.

依赖倒置原则的中心思想是面向接口编程,依赖于抽象而不依赖于具体

设计模式的分类

Java 中一般认为有 23 种设计模式,总体来说设计模式分为三大类:

创建型模式,共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式.

结构型模式,共7种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式.

行为型模式,共11种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式.

一、创建型模式

1. 工厂方法模式

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式.

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象.

代码实现:

  1. 创建一个接口

public interface Shape {
void draw();
}


2. 创建实现接口的实体类

```java
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()");
   }
}
  1. 创建一个工厂,生成基于给定信息的实体类的对象

    public class ShapeFactory {
        
       //使用 getShape 方法获取形状类型的对象
       public Shape getShape(String shapeType){
          if(shapeType == null){
             return null;
          }        
          if(shapeType.equalsIgnoreCase("CIRCLE")){
             return new Circle();
          } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
             return new Rectangle();
          } else if(shapeType.equalsIgnoreCase("SQUARE")){
             return new Square();
          }
          return null;
       }
    }
    
  2. 使用该工厂,通过传递类型信息来获取实体类的对象

    public class FactoryPatternDemo {
     
       public static void main(String[] args) {
          ShapeFactory shapeFactory = new ShapeFactory();
     
          //获取 Circle 的对象,并调用它的 draw 方法
          Shape shape1 = shapeFactory.getShape("CIRCLE");
     
          //调用 Circle 的 draw 方法
          shape1.draw();
     
          //获取 Rectangle 的对象,并调用它的 draw 方法
          Shape shape2 = shapeFactory.getShape("RECTANGLE");
     
          //调用 Rectangle 的 draw 方法
          shape2.draw();
     
          //获取 Square 的对象,并调用它的 draw 方法
          Shape shape3 = shapeFactory.getShape("SQUARE");
     
          //调用 Square 的 draw 方法
          shape3.draw();
       }
    }
    
  3. 执行程序,输出结果

    Circle.draw()
    Circle.draw()
    Circle.draw()

工厂方法模式是对简单工厂模式进一步的解耦,在工厂方法模式中是一类产品对应一个工厂类,而这些工厂类都继承于一个抽象工厂.这相当于是把原本会随着业务扩展而庞大的简单工厂类,拆分成了一个个的具体产品工厂类,这样代码就不会都耦合在同一个类里.

优点

  1. 良好的封装性,代码结构清晰
  2. 代码的扩展性大大提高
  3. 屏蔽了产品类

缺点

在一定程度上增加了代码的复杂度

2. 抽象工厂模式

抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂.该超级工厂又称为其他工厂的工厂.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式.

在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类.每个生成的工厂都能按照工厂模式提供对象.

代码实现

  1. 为形状创建一个接口

    public interface Shape {
       void draw();
    }
    
  2. 创建实现接口的实体类

    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()");
       }
    }
    
  3. 为颜色创建一个接口

    public interface Color {
       void fill();
    }
    
  4. 创建实现接口的实体类

    public class Red implements Color {
     
       @Override
       public void fill() {
          System.out.println("Red.fill()");
       }
    }
    
    public class Green implements Color {
     
       @Override
       public void fill() {
          System.out.println("Green.fill()");
       }
    }
    
    public class Blue implements Color {
     
       @Override
       public void fill() {
          System.out.println("Blue.fill()");
       }
    }
    
  5. 为 Color 和 Shape 对象创建抽象类来获取工厂

    public abstract class AbstractFactory {
       public abstract Color getColor(String color);
       public abstract Shape getShape(String shape);
    }
    
  6. 创建扩展了 AbstractFactory 的工厂类,基于给定的信息生成实体类的对象

    public class ShapeFactory extends AbstractFactory {
        
       @Override
       public Shape getShape(String shapeType){
          if(shapeType == null){
             return null;
          }        
          if(shapeType.equalsIgnoreCase("CIRCLE")){
             return new Circle();
          } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
             return new Rectangle();
          } else if(shapeType.equalsIgnoreCase("SQUARE")){
             return new Square();
          }
          return null;
       }
       
       @Override
       public Color getColor(String color) {
          return null;
       }
    }
    
    public class ColorFactory extends AbstractFactory {
        
       @Override
       public Shape getShape(String shapeType){
          return null;
       }
       
       @Override
       public Color getColor(String color) {
          if(color == null){
             return null;
          }        
          if(color.equalsIgnoreCase("RED")){
             return new Red();
          } else if(color.equalsIgnoreCase("GREEN")){
             return new Green();
          } else if(color.equalsIgnoreCase("BLUE")){
             return new Blue();
          }
          return null;
       }
    }
    
  7. 创建一个工厂创造器/生成器类,通过传递形状或颜色信息来获取工厂

    public class FactoryProducer {
       public static AbstractFactory getFactory(String choice){
          if(choice.equalsIgnoreCase("SHAPE")){
             return new ShapeFactory();
          } else if(choice.equalsIgnoreCase("COLOR")){
             return new ColorFactory();
          }
          return null;
       }
    }
    
  8. 使用 FactoryProducer 来获取 AbstractFactory,通过传递类型信息来获取实体类的对象

    public class AbstractFactoryPatternDemo {
       public static void main(String[] args) {
     
          //获取形状工厂
          AbstractFactory shapeFactory = FactoryProducer.getFactory("SHAPE");
     
          //获取形状为 Circle 的对象
          Shape shape1 = shapeFactory.getShape("CIRCLE");
     
          //调用 Circle 的 draw 方法
          shape1.draw();
     
          //获取形状为 Rectangle 的对象
          Shape shape2 = shapeFactory.getShape("RECTANGLE");
     
          //调用 Rectangle 的 draw 方法
          shape2.draw();
          
          //获取形状为 Square 的对象
          Shape shape3 = shapeFactory.getShape("SQUARE");
     
          //调用 Square 的 draw 方法
          shape3.draw();
     
          //获取颜色工厂
          AbstractFactory colorFactory = FactoryProducer.getFactory("COLOR");
     
          //获取颜色为 Red 的对象
          Color color1 = colorFactory.getColor("RED");
     
          //调用 Red 的 fill 方法
          color1.fill();
     
          //获取颜色为 Green 的对象
          Color color2 = colorFactory.getColor("GREEN");
     
          //调用 Green 的 fill 方法
          color2.fill();
     
          //获取颜色为 Blue 的对象
          Color color3 = colorFactory.getColor("BLUE");
     
          //调用 Blue 的 fill 方法
          color3.fill();
       }
    }
    
  9. 执行程序,输出结果

    Circle.draw()
    Rectangle.draw()
    Square.draw()
    Red.fill()
    Green.fill()
    Blue.fill()

优点

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

缺点

产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的Creator里添加代码,又要在具体的里面添加代码

3. 单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式.

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建.这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象.

特点

  1. 单例类只能有一个实例
  2. 单例类必须自己创建自己的唯一实例
  3. 单例类必须给所有其他对象提供这一实例

代码实现

  • 懒汉式(线程不安全)

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

    若想使懒汉式单例线程安全,需要添加synchronized

  • 饿汉式

    public class Singleton {  
        private static Singleton instance = new Singleton();  
        private Singleton (){}  
        public static Singleton getInstance() {  
        return instance;  
        }  
    }
    
  • 双检锁

    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;  
        }  
    }
    
  • 登记式

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

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

优点

  1. 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例
  2. 避免对资源的多重占用

缺点

没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎样来实例化

4. 建造者模式

建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式.

一个 Builder 类会一步一步构造最终的对象.该 Builder 类是独立于其他对象的.

代码实现

  1. 创建一个表示食物条目和食物包装的接口

    public interface Item {
       public String name();
       public Packing packing();
       public float price();
    }
    
    public interface Packing {
       public String pack();
    }
    
  2. 创建实现 Packing 接口的实体类

    public class Wrapper implements Packing {
     
       @Override
       public String pack() {
          return "Wrapper";
       }
    }
    
    public class Bottle implements Packing {
     
       @Override
       public String pack() {
          return "Bottle";
       }
    }
    
  3. 创建实现 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();
    }
    
  4. 创建扩展了 Burger 和 ColdDrink 的实体类

    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";
       }
    }
    
  5. 创建一个 Meal 类,带有上面定义的 Item 对象

    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());
          }        
       }    
    }
    
  6. 创建一个 MealBuilder 类,实际的 builder 类负责创建 Meal 对象

    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;
       }
    }
    
  7. BuiderPatternDemo 使用 MealBuilder 来演示建造者模式

    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());
       }
    }
    
  8. 执行程序,输出结果

    Veg Meal
    Item : Veg Burger, Packing : Wrapper, Price : 25.0
    Item : Coke, Packing : Bottle, Price : 30.0
    Total Cost: 55.0

    Non-Veg Meal
    Item : Chicken Burger, Packing : Wrapper, Price : 50.5
    Item : Pepsi, Packing : Bottle, Price : 35.0
    Total Cost: 85.5

优点

  1. 建造者独立,易扩展
  2. 便于控制细节风险

缺点

  1. 产品必须有共同点,范围有限制
  2. 如内部变化复杂,会有很多的建造类

5. 原型模式

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式.

这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆.当直接创建对象的代价比较大时,则采用这种模式.例如,一个对象需要在一个高代价的数据库操作之后被创建.我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用.

代码实现

  1. 创建一个实现了 Cloneable接口的抽象类

    public abstract class Shape implements Cloneable {
       
       private String id;
       protected String type;
       
       abstract void draw();
       
       public String getType(){
          return type;
       }
       
       public String getId() {
          return id;
       }
       
       public void setId(String id) {
          this.id = id;
       }
       
       public Object clone() {
          Object clone = null;
          try {
             clone = super.clone();
          } catch (CloneNotSupportedException e) {
             e.printStackTrace();
          }
          return clone;
       }
    }
    
  2. 创建扩展了上面抽象类的实体类

    public class Rectangle extends Shape {
     
       public Rectangle(){
         type = "Rectangle";
       }
     
       @Override
       public void draw() {
          System.out.println("Rectangle.draw()");
       }
    }
    
    public class Square extends Shape {
     
       public Square(){
         type = "Square";
       }
     
       @Override
       public void draw() {
          System.out.println("Square.draw()");
       }
    }
    
    public class Circle extends Shape {
     
       public Circle(){
         type = "Circle";
       }
     
       @Override
       public void draw() {
          System.out.println("Circle.draw()");
       }
    }
    
  3. 创建一个类,从数据库获取实体类,并把它们存储在一个 Hashtable

    public class ShapeCache {
        
       private static Hashtable<String, Shape> shapeMap 
          = new Hashtable<String, Shape>();
     
       public static Shape getShape(String shapeId) {
          Shape cachedShape = shapeMap.get(shapeId);
          return (Shape) cachedShape.clone();
       }
     
       public static void loadCache() {
          Circle circle = new Circle();
          circle.setId("1");
          shapeMap.put(circle.getId(),circle);
     
          Square square = new Square();
          square.setId("2");
          shapeMap.put(square.getId(),square);
     
          Rectangle rectangle = new Rectangle();
          rectangle.setId("3");
          shapeMap.put(rectangle.getId(),rectangle);
       }
    }
    
  4. PrototypePatternDemo 使用 ShapeCache 类来获取存储在 Hashtable 中的形状的克隆

    public class PrototypePatternDemo {
       public static void main(String[] args) {
          ShapeCache.loadCache();
     
          Shape clonedShape = (Shape) ShapeCache.getShape("1");
          System.out.println("Shape : " + clonedShape.getType());        
     
          Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
          System.out.println("Shape : " + clonedShape2.getType());        
     
          Shape clonedShape3 = (Shape) ShapeCache.getShape("3");
          System.out.println("Shape : " + clonedShape3.getType());        
       }
    }
    
  5. 执行程序,输出结果

    Shape : Circle

    Shape : Square

    Shape : Rectangle

优点

  1. 性能提高
  2. 逃避构造函数的约束

缺点

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

二、结构型模式

1. 适配器模式

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

这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能.

代码实现

  1. 为媒体播放器和更高级的媒体播放器创建接口

    public interface MediaPlayer {
       public void play(String audioType, String fileName);
    }
    
    public interface AdvancedMediaPlayer { 
       public void playVlc(String fileName);
       public void playMp4(String fileName);
    }
    
  2. 创建实现了 AdvancedMediaPlayer 接口的实体类

    public class VlcPlayer implements AdvancedMediaPlayer{
       @Override
       public void playVlc(String fileName) {
          System.out.println("Playing vlc file.Name: "+ fileName);      
       }
     
       @Override
       public void playMp4(String fileName) {
          // 什么也不做
       }
    }
    
    public class Mp4Player implements AdvancedMediaPlayer{
     
       @Override
       public void playVlc(String fileName) {
          // 什么也不做
       }
     
       @Override
       public void playMp4(String fileName) {
          System.out.println("Playing mp4 file.Name: "+ fileName);      
       }
    }
    
  3. 创建实现了 MediaPlayer接口的适配器类

    public class MediaAdapter implements MediaPlayer {
     
       AdvancedMediaPlayer advancedMusicPlayer;
     
       public MediaAdapter(String audioType){
          if(audioType.equalsIgnoreCase("vlc") ){
             advancedMusicPlayer = new VlcPlayer();       
          } else if (audioType.equalsIgnoreCase("mp4")){
             advancedMusicPlayer = new Mp4Player();
          }  
       }
     
       @Override
       public void play(String audioType, String fileName) {
          if(audioType.equalsIgnoreCase("vlc")){
             advancedMusicPlayer.playVlc(fileName);
          }else if(audioType.equalsIgnoreCase("mp4")){
             advancedMusicPlayer.playMp4(fileName);
          }
       }
    }
    
  4. 创建实现了 MediaPlayer接口的实体类

    public class AudioPlayer implements MediaPlayer {
       MediaAdapter mediaAdapter; 
     
       @Override
       public void play(String audioType, String fileName) {    
     
          //播放 mp3 音乐文件的内置支持
          if(audioType.equalsIgnoreCase("mp3")){
             System.out.println("Playing mp3 file. Name: "+ fileName);         
          } 
          //mediaAdapter 提供了播放其他文件格式的支持
          else if(audioType.equalsIgnoreCase("vlc") 
             || audioType.equalsIgnoreCase("mp4")){
             mediaAdapter = new MediaAdapter(audioType);
             mediaAdapter.play(audioType, fileName);
          }
          else{
             System.out.println("Invalid media. "+
                audioType + " format not supported");
          }
       }   
    }
    
  5. 使用AudioPlayer 来播放不同类型的音频格式

    public class AdapterPatternDemo {
       public static void main(String[] args) {
          AudioPlayer audioPlayer = new AudioPlayer();
     
          audioPlayer.play("mp3", "beyond the horizon.mp3");
          audioPlayer.play("mp4", "alone.mp4");
          audioPlayer.play("vlc", "far far away.vlc");
          audioPlayer.play("avi", "mind me.avi");
       }
    }
    
  6. 执行程序,输出结果

    Playing mp3 file.Name: beyond the horizon.mp3

    Playing mp4 file.Name: alone.mp4

    Playing vlc file.Name: far far away.vlc

    Invalid media. avi format not supported

优点

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

缺点

  1. 过多的使用适配器,会让系统非常凌乱,不易整体进行把握
  2. 只能适配一个适配者类,而且目标类必须是抽象类

2. 装饰器模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装.

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能.

代码实现

  1. 创建一个接口

    public interface Shape {
       void draw();
    }
    
  2. 创建实现接口的实体类

    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");
       }
    }
    
  3. 创建实现了 Shape 接口的抽象装饰类

    public abstract class ShapeDecorator implements Shape {
       protected Shape decoratedShape;
     
       public ShapeDecorator(Shape decoratedShape){
          this.decoratedShape = decoratedShape;
       }
     
       public void draw(){
          decoratedShape.draw();
       }  
    }
    
  4. 创建扩展了 ShapeDecorator 类的实体装饰类

    public class RedShapeDecorator extends ShapeDecorator {
     
       public RedShapeDecorator(Shape decoratedShape) {
          super(decoratedShape);     
       }
     
       @Override
       public void draw() {
          decoratedShape.draw();         
          setRedBorder(decoratedShape);
       }
     
       private void setRedBorder(Shape decoratedShape){
          System.out.println("Border Color: Red");
       }
    }
    
  5. 使用 RedShapeDecorator 来装饰 Shape 对象

    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());
          //Shape redCircle = new RedShapeDecorator(new Circle());
          //Shape redRectangle = new RedShapeDecorator(new Rectangle());
          System.out.println("Circle with normal border");
          circle.draw();
     
          System.out.println("\nCircle of red border");
          redCircle.draw();
     
          System.out.println("\nRectangle of red border");
          redRectangle.draw();
       }
    }
    
  6. 执行程序,输出结果

    Circle with normal border
    Shape: Circle

    Circle of red border
    Shape: Circle
    Border Color: Red

    Rectangle of red border
    Shape: Rectangle
    Border Color: Red

优点

装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态的扩展一个实现类的功能

缺点

多层装饰比较复杂

3. 代理模式

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能.这种类型的设计模式属于结构型模式.

在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口.

代码实现

  1. 创建一个接口

    public interface Image {
       void display();
    }
    
  2. 创建实现接口的实体类

    public class RealImage implements Image {
     
       private String fileName;
     
       public RealImage(String fileName){
          this.fileName = fileName;
          loadFromDisk(fileName);
       }
     
       @Override
       public void display() {
          System.out.println("Displaying " + fileName);
       }
     
       private void loadFromDisk(String fileName){
          System.out.println("Loading " + fileName);
       }
    }
    
    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();
       }
    }
    
  3. 当被请求时,使用 ProxyImage 来获取 RealImage 类的对象

    public class ProxyPatternDemo {
       
       public static void main(String[] args) {
          Image image = new ProxyImage("test_10mb.jpg");
     
          // 图像将从磁盘加载
          image.display(); 
          System.out.println("");
          // 图像不需要从磁盘加载
          image.display();  
       }
    }
    
  4. 执行程序,输出结果

    Loading test_10mb.jpg
    Displaying test_10mb.jpg

    Displaying test_10mb.jpg

优点

  1. 职责清晰
  2. 高扩展性
  3. 智能化

缺点

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

4. 外观模式

外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口.这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性.

这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用.

代码实现

  1. 为形状创建一个接口

    public interface Shape {
       void draw();
    }
    
  2. 创建实现接口的实体类

    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()");
       }
    }
    
  3. 创建一个外观类

    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();
       }
    }
    
  4. 使用该外观类画出各种类型的形状

    public class FacadePatternDemo {
       public static void main(String[] args) {
          ShapeMaker shapeMaker = new ShapeMaker();
     
          shapeMaker.drawCircle();
          shapeMaker.drawRectangle();
          shapeMaker.drawSquare();      
       }
    }
    
  5. 执行程序,输出结果

    Rectangle.draw()

    Square.draw()

    Circle.draw()

优点

  1. 减少系统相互依赖
  2. 提高灵活性
  3. 提高了安全性

缺点

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

5. 桥接模式

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

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

代码实现

  1. 创建桥接实现接口

    public interface DrawAPI {
       public void drawCircle(int radius, int x, int y);
    }
    
  2. 创建实现了 DrawAPI接口的实体桥接实现类

    public class RedCircle implements DrawAPI {
       @Override
       public void drawCircle(int radius, int x, int y) {
          System.out.println("Drawing Circle[ color: red, radius: "
             + radius +", x: " +x+", "+ y +"]");
       }
    }
    
    public class GreenCircle implements DrawAPI {
       @Override
       public void drawCircle(int radius, int x, int y) {
          System.out.println("Drawing Circle[ color: green, radius: "
             + radius +", x: " +x+", "+ y +"]");
       }
    }
    
  3. 使用 DrawAPI 接口创建抽象类 Shape

    public abstract class Shape {
       protected DrawAPI drawAPI;
       protected Shape(DrawAPI drawAPI){
          this.drawAPI = drawAPI;
       }
       public abstract void draw();  
    }
    
  4. 创建实现了 Shape 抽象类的实体类

    public class Circle extends Shape {
       private int x, y, radius;
     
       public Circle(int x, int y, int radius, DrawAPI drawAPI) {
          super(drawAPI);
          this.x = x;  
          this.y = y;  
          this.radius = radius;
       }
     
       @Override
       public void draw() {
          drawAPI.drawCircle(radius,x,y);
       }
    }
    
  5. 使用 ShapeDrawAPI 类画出不同颜色的圆

    public class BridgePatternDemo {
       public static void main(String[] args) {
          Shape redCircle = new Circle(100,100, 10, new RedCircle());
          Shape greenCircle = new Circle(100,100, 10, new GreenCircle());
     
          redCircle.draw();
          greenCircle.draw();
       }
    }
    
  6. 执行程序,输出结果

    Drawing Circle[ color: red, radius: 10, x: 100, 100]
    Drawing Circle[ color: green, radius: 10, x: 100, 100]

优点

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

缺点

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

6. 组合模式

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

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

代码实现

  1. 创建 Employee 类,该类带有 Employee 对象的列表

    public class Employee {
       private String name;
       private String dept;
       private int salary;
       private List<Employee> subordinates;
     
       public Employee(String name,String dept, int sal) {
          this.name = name;
          this.dept = dept;
          this.salary = sal;
          subordinates = new ArrayList<Employee>();
       }
     
       public void add(Employee e) {
          subordinates.add(e);
       }
     
       public void remove(Employee e) {
          subordinates.remove(e);
       }
     
       public List<Employee> getSubordinates(){
         return subordinates;
       }
     
       @Override
       public String toString(){
          return ("Employee :[ Name : "+ name 
          +", dept : "+ dept + ", salary :"
          + salary+" ]");
       }   
    }
    
  2. 使用 Employee 类来创建和打印员工的层次结构

    public class CompositePatternDemo {
       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.add(headSales);
          CEO.add(headMarketing);
     
          headSales.add(salesExecutive1);
          headSales.add(salesExecutive2);
     
          headMarketing.add(clerk1);
          headMarketing.add(clerk2);
     
          //打印该组织的所有员工
          System.out.println(CEO); 
          for (Employee headEmployee : CEO.getSubordinates()) {
             System.out.println(headEmployee);
             for (Employee employee : headEmployee.getSubordinates()) {
                System.out.println(employee);
             }
          }        
       }
    }
    
  3. 执行程序,输出结果

    Employee :[ Name : John, dept : CEO, salary :30000 ]
    Employee :[ Name : Robert, dept : Head Sales, salary :20000 ]
    Employee :[ Name : Richard, dept : Sales, salary :10000 ]
    Employee :[ Name : Rob, dept : Sales, salary :10000 ]
    Employee :[ Name : Michel, dept : Head Marketing, salary :20000 ]
    Employee :[ Name : Laura, dept : Marketing, salary :10000 ]
    Employee :[ Name : Bob, dept : Marketing, salary :10000 ]

优点

  1. 高层模块调用简单
  2. 节点自由添加

缺点

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

7. 享元模式

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能.这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式.

享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象.

代码实现

  1. 创建一个接口

    public interface Shape {
       void draw();
    }
    
  2. 创建实现接口的实体类

    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);
       }
    }
    
  3. 创建一个工厂,生成基于给定信息的实体类的对象

    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("Creating circle of color : " + color);
          }
          return circle;
       }
    }
    
  4. 使用该工厂,通过传递颜色信息来获取实体类的对象

    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);
       }
    }
    
  5. 执行程序,输出结果

    Creating circle of color : Blue
    Circle.Draw() [Color : Blue, x : 53, y :2, radius :100
    Creating circle of color : Black
    Circle.Draw() [Color : Black, x : 59, y :48, radius :100
    Circle.Draw() [Color : Black, x : 12, y :46, radius :100
    Circle.Draw() [Color : Blue, x : 36, y :34, radius :100
    Creating circle of color : White
    Circle.Draw() [Color : White, x : 62, y :14, radius :100
    Circle.Draw() [Color : Black, x : 56, y :85, radius :100
    Circle.Draw() [Color : White, x : 26, y :48, radius :100
    Circle.Draw() [Color : Black, x : 92, y :18, radius :100
    Circle.Draw() [Color : White, x : 74, y :76, radius :100
    Creating circle of color : Red
    Circle.Draw() [Color : Red, x : 8, y :49, radius :100
    Circle.Draw() [Color : White, x : 60, y :85, radius :100
    Circle.Draw() [Color : Black, x : 71, y :54, radius :100
    Creating circle of color : Green
    Circle.Draw() [Color : Green, x : 43, y :15, radius :100
    Circle.Draw() [Color : Green, x : 13, y :87, radius :100
    Circle.Draw() [Color : Black, x : 30, y :72, radius :100
    Circle.Draw() [Color : Red, x : 35, y :50, radius :100
    Circle.Draw() [Color : Green, x : 4, y :2, radius :100
    Circle.Draw() [Color : Green, x : 76, y :80, radius :100
    Circle.Draw() [Color : Blue, x : 2, y :10, radius :100
    Circle.Draw() [Color : Black, x : 57, y :10, radius :100

优点

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

缺点

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

三、行为型模式

1. 策略模式

在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改.这种类型的设计模式属于行为型模式.

代码实现

  1. 创建一个接口

    public interface Strategy {
       public int doOperation(int num1, int num2);
    }
    
  2. 创建实现接口的实体类

    public class OperationAdd implements Strategy{
       @Override
       public int doOperation(int num1, int num2) {
          return num1 + num2;
       }
    }
    
    public class OperationSubtract implements Strategy{
       @Override
       public int doOperation(int num1, int num2) {
          return num1 - num2;
       }
    }
    
    public class OperationMultiply implements Strategy{
       @Override
       public int doOperation(int num1, int num2) {
          return num1 * num2;
       }
    }
    
  3. 创建 Context

    public class Context {
       private Strategy strategy;
     
       public Context(Strategy strategy){
          this.strategy = strategy;
       }
     
       public int executeStrategy(int num1, int num2){
          return strategy.doOperation(num1, num2);
       }
    }
    
  4. 使用 Context 来查看当它改变策略 Strategy时的行为变化

    public class StrategyPatternDemo {
       public static void main(String[] args) {
          Context context = new Context(new OperationAdd());    
          System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
     
          context = new Context(new OperationSubtract());      
          System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
     
          context = new Context(new OperationMultiply());    
          System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
       }
    }
    
  5. 执行程序,输出结果

    10 + 5 = 15
    10 - 5 = 5
    10 * 5 = 50

优点

  1. 算法可以自由切换
  2. 避免使用多重条件判断
  3. 扩展性好

缺点

  1. 策略类会增多
  2. 所以策略类都需要对外暴露

2. 模板方法模式

在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板.它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行.这种类型的设计模式属于行为型模式.

代码实现

  1. 创建一个抽象类,它的模板方法被设置为 final

    public abstract class Game {
       abstract void initialize();
       abstract void startPlay();
       abstract void endPlay();
     
       //模板
       public final void play(){
     
          //初始化游戏
          initialize();
     
          //开始游戏
          startPlay();
     
          //结束游戏
          endPlay();
       }
    }
    
  2. 创建扩展了上述类的实体类

    public class Cricket extends Game {
     
       @Override
       void endPlay() {
          System.out.println("Cricket Game Finished!");
       }
     
       @Override
       void initialize() {
          System.out.println("Cricket Game Initialized! Start playing.");
       }
     
       @Override
       void startPlay() {
          System.out.println("Cricket Game Started. Enjoy the game!");
       }
    }
    
    public class Football extends Game {
     
       @Override
       void endPlay() {
          System.out.println("Football Game Finished!");
       }
     
       @Override
       void initialize() {
          System.out.println("Football Game Initialized! Start playing.");
       }
     
       @Override
       void startPlay() {
          System.out.println("Football Game Started. Enjoy the game!");
       }
    }
    
  3. 使用 Game的模板方法 play() 来演示游戏的定义方式

    public class TemplatePatternDemo {
       public static void main(String[] args) {
          Game game = new Cricket();
          game.play();
          System.out.println();
          game = new Football();
          game.play();      
       }
    }
    
  4. 执行程序,输出结果

    Cricket Game Initialized! Start playing.
    Cricket Game Started. Enjoy the game!
    Cricket Game Finished!

    Football Game Initialized! Start playing.
    Football Game Started. Enjoy the game!
    Football Game Finished!

优点

  1. 封装不变部分,扩展可变部分
  2. 提取公共代码,便于维护
  3. 行为由父类控制,子类实现

缺点

每一个不同的实现都需要一个子类来实现,导致类的个数增加,是的系统更加庞大

3. 观察者模式

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern).比如,当一个对象被修改时,则会自动通知依赖它的对象.观察者模式属于行为型模式.

代码实现

  1. 创建 Subject

    public class Subject {
       
       private List<Observer> observers = new ArrayList<Observer>();
       private int state;
     
       public int getState() {
          return state;
       }
     
       public void setState(int state) {
          this.state = state;
          notifyAllObservers();
       }
     
       public void attach(Observer observer){
          observers.add(observer);      
       }
     
       public void notifyAllObservers(){
          for (Observer observer : observers) {
             observer.update();
          }
       }  
    }
    
  2. 创建 Observer

    public abstract class Observer {
       protected Subject subject;
       public abstract void update();
    }
    
  3. 创建实体观察者类

    public class BinaryObserver extends Observer{
     
       public BinaryObserver(Subject subject){
          this.subject = subject;
          this.subject.attach(this);
       }
     
       @Override
       public void update() {
          System.out.println( "Binary String: " 
          + Integer.toBinaryString( subject.getState() ) ); 
       }
    }
    
    public class OctalObserver extends Observer{
     
       public OctalObserver(Subject subject){
          this.subject = subject;
          this.subject.attach(this);
       }
     
       @Override
       public void update() {
         System.out.println( "Octal String: " 
         + Integer.toOctalString( subject.getState() ) ); 
       }
    }
    
    public class HexaObserver extends Observer{
     
       public HexaObserver(Subject subject){
          this.subject = subject;
          this.subject.attach(this);
       }
     
       @Override
       public void update() {
          System.out.println( "Hex String: " 
          + Integer.toHexString( subject.getState() ).toUpperCase() ); 
       }
    }
    
  4. 使用 Subject 和实体观察者对象

    public class ObserverPatternDemo {
       public static void main(String[] args) {
          Subject subject = new Subject();
     
          new HexaObserver(subject);
          new OctalObserver(subject);
          new BinaryObserver(subject);
     
          System.out.println("First state change: 15");   
          subject.setState(15);
          System.out.println("Second state change: 10");  
          subject.setState(10);
       }
    }
    
  5. 执行程序,输出结果

    First state change: 15
    Hex String: F
    Octal String: 17
    Binary String: 1111
    Second state change: 10
    Hex String: A
    Octal String: 12
    Binary String: 1010

优点

  1. 观察者和被观察者是抽象耦合的
  2. 建立一套触发机制

缺点

  1. 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有观察者都通知到会花费很多时间
  2. 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发他们之间进行循环调用,可能导致系统崩溃
  3. 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道目标发生了变化

4. 迭代器模式

迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式.这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示.迭代器模式属于行为型模式.

代码实现

  1. 创建接口

    public interface Iterator {
        boolean hasNext();
    
        Object next();
    }
    
    public interface Container {
        Iterator getIterator();
    }
    
  2. 创建实现了Container 接口的实体类.该类有实现了 Iterator 接口的内部类 NameIterator

    public class NameRepository implements Container {
       public String[] names = {"Robert" , "John" ,"Julie" , "Lora"};
     
       @Override
       public Iterator getIterator() {
          return new NameIterator();
       }
     
       private class NameIterator implements Iterator {
     
          int index;
     
          @Override
          public boolean hasNext() {
             if(index < names.length){
                return true;
             }
             return false;
          }
     
          @Override
          public Object next() {
             if(this.hasNext()){
                return names[index++];
             }
             return null;
          }     
       }
    }
    
  3. 使用 NameRepository 来获取迭代器,并打印名字

    public class IteratorPatternDemo {
        public static void main(String[] args) {
            NameRepository namesRepository = new NameRepository();
    
            for (Iterator iter = namesRepository.getIterator(); iter.hasNext(); ) {
                String name = (String) iter.next();
                System.out.println("Name : " + name);
            }
        }
    }
    
  4. 执行程序,输出结果

    Name : Robert
    Name : John
    Name : Julie
    Name : Lora

优点

  1. 它支持不同的方式遍历一个聚合对象
  2. 迭代器简化了聚合类
  3. 在同一聚合上可以有多个遍历
  4. 在迭代器模式中,增加新的聚合类和迭代器类都很方便,无需修改原有代码

缺点

由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性

5. 责任链模式

顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链.这种模式给予请求的类型,对请求的发送者和接收者进行解耦.这种类型的设计模式属于行为型模式.

在这种模式中,通常每个接收者都包含对另一个接收者的引用.如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推.

代码实现

  1. 创建抽象的记录器类

    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 logMessage(int level, String message){
          if(this.level <= level){
             write(message);
          }
          if(nextLogger !=null){
             nextLogger.logMessage(level, message);
          }
       }
     
       abstract protected void write(String message);
       
    }
    
  2. 创建扩展了该记录器类的实体类

    public class ConsoleLogger extends AbstractLogger {
     
       public ConsoleLogger(int level){
          this.level = level;
       }
     
       @Override
       protected void write(String message) {    
          System.out.println("Standard Console::Logger: " + message);
       }
    }
    
    public class ErrorLogger extends AbstractLogger {
     
       public ErrorLogger(int level){
          this.level = level;
       }
     
       @Override
       protected void write(String message) {    
          System.out.println("Error Console::Logger: " + message);
       }
    }
    
    public class FileLogger extends AbstractLogger {
     
       public FileLogger(int level){
          this.level = level;
       }
     
       @Override
       protected void write(String message) {    
          System.out.println("File::Logger: " + message);
       }
    }
    
  3. 创建不同类型的记录器.赋予它们不同的错误级别,并在每个记录器中设置下一个记录器.每个记录器中的下一个记录器代表的是链的一部分

    public class ChainPatternDemo {
       
       private static AbstractLogger getChainOfLoggers(){
     
          AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
          AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
          AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
     
          errorLogger.setNextLogger(fileLogger);
          fileLogger.setNextLogger(consoleLogger);
     
          return errorLogger;  
       }
     
       public static void main(String[] args) {
          AbstractLogger loggerChain = getChainOfLoggers();
     
          loggerChain.logMessage(AbstractLogger.INFO, "This is an information.");
     
          loggerChain.logMessage(AbstractLogger.DEBUG, 
             "This is a debug level information.");
     
          loggerChain.logMessage(AbstractLogger.ERROR, 
             "This is an error information.");
       }
    }
    
  4. 执行程序,输出结果

    Standard Console::Logger: This is an information.
    File::Logger: This is a debug level information.
    Standard Console::Logger: This is a debug level information.
    Error Console::Logger: This is an error information.
    File::Logger: This is an error information.
    Standard Console::Logger: This is an error information.

优点

  1. 降低耦合度,它将请求的发送者和接受者解耦
  2. 简化了对象,是的对象不需要知道链的结构
  3. 增强给对象指派职责的灵活性,通过改变链内的成员或者调动它们的次序,允许动态的新增或者删除职责
  4. 增加新的请求处理类很方便

缺点

  1. 不能保证请求一定被接收
  2. 系统性能将收到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用
  3. 可能不太容易观察运行时的特征,有碍于除错

6. 命令模式

命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式.请求以命令的形式包裹在对象中,并传给调用对象.调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令.

代码实现

  1. 创建一个命令接口

    public interface Order {
       void execute();
    }
    
  2. 创建一个请求类

    public class Stock {
       
       private String name = "ABC";
       private int quantity = 10;
     
       public void buy(){
          System.out.println("Stock [ Name: "+name+", Quantity: " + quantity +" ] bought");
       }
       public void sell(){
          System.out.println("Stock [ Name: "+name+", Quantity: " + quantity +" ] sold");
       }
    }
    
  3. 创建实现了 Order 接口的实体类

    public class BuyStock implements Order {
       private Stock abcStock;
     
       public BuyStock(Stock abcStock){
          this.abcStock = abcStock;
       }
        
       @Override
       public void execute() {
          abcStock.buy();
       }
    }
    
    public class SellStock implements Order {
       private Stock abcStock;
     
       public SellStock(Stock abcStock){
          this.abcStock = abcStock;
       }
     
       @Override
       public void execute() {
          abcStock.sell();
       }
    }
    
  4. 创建命令调用类

    public class Broker {
       private List<Order> orderList = new ArrayList<Order>(); 
     
       public void takeOrder(Order order){
          orderList.add(order);      
       }
     
       public void placeOrders(){
          for (Order order : orderList) {
             order.execute();
          }
          orderList.clear();
       }
    }
    
  5. 使用 Broker 类来接受并执行命令

    public class CommandPatternDemo {
       public static void main(String[] args) {
          Stock abcStock = new Stock();
     
          BuyStock buyStockOrder = new BuyStock(abcStock);
          SellStock sellStockOrder = new SellStock(abcStock);
     
          Broker broker = new Broker();
          broker.takeOrder(buyStockOrder);
          broker.takeOrder(sellStockOrder);
     
          broker.placeOrders();
       }
    }
    
  6. 执行程序,输出结果

    Stock [ Name: ABC, Quantity:10 ]bought
    Stock [ Name: ABC, Quantity:10 ]sold

优点

  1. 降低了系统耦合度
  2. 新的命令可以很容易添加到系统中去

缺点

使用命令模式可能会导致某些系统有过多的具体命令

7. 备忘录模式

备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象.备忘录模式属于行为型模式.

代码实现

  1. 创建 Memento

    public class Memento {
       private String state;
     
       public Memento(String state){
          this.state = state;
       }
     
       public String getState(){
          return state;
       }  
    }
    
  2. 创建 Originator

    public class Originator {
       private String state;
     
       public void setState(String state){
          this.state = state;
       }
     
       public String getState(){
          return state;
       }
     
       public Memento saveStateToMemento(){
          return new Memento(state);
       }
     
       public void getStateFromMemento(Memento Memento){
          state = Memento.getState();
       }
    }
    
  3. 创建 CareTaker

    public class CareTaker {
       private List<Memento> mementoList = new ArrayList<Memento>();
     
       public void add(Memento state){
          mementoList.add(state);
       }
     
       public Memento get(int index){
          return mementoList.get(index);
       }
    }
    
  4. 使用 CareTakerOriginator 对象

    public class MementoPatternDemo {
       public static void main(String[] args) {
          Originator originator = new Originator();
          CareTaker careTaker = new CareTaker();
          originator.setState("State #1");
          originator.setState("State #2");
          careTaker.add(originator.saveStateToMemento());
          originator.setState("State #3");
          careTaker.add(originator.saveStateToMemento());
          originator.setState("State #4");
     
          System.out.println("Current State: " + originator.getState());    
          originator.getStateFromMemento(careTaker.get(0));
          System.out.println("First saved State: " + originator.getState());
          originator.getStateFromMemento(careTaker.get(1));
          System.out.println("Second saved State: " + originator.getState());
       }
    }
    
  5. 执行程序,输出结果

    Current State: State #4
    First saved State: State #2
    Second saved State: State #3

优点

  1. 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便的回到某个历史的状态
  2. 实现了信息的封装,使得用户不需要关心状态的保存细节

缺点

消耗资源,如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存

8. 状态模式

在状态模式(State Pattern)中,类的行为是基于它的状态改变的.这种类型的设计模式属于行为型模式.

代码实现

  1. 创建一个接口

    public interface State {
       void doAction(Context context);
    }
    
  2. 创建实现接口的实体类

    public class StartState implements State {
     
       public void doAction(Context context) {
          System.out.println("Player is in start state");
          context.setState(this); 
       }
     
       public String toString(){
          return "Start State";
       }
    }
    
    public class StopState implements State {
     
       public void doAction(Context context) {
          System.out.println("Player is in stop state");
          context.setState(this); 
       }
     
       public String toString(){
          return "Stop State";
       }
    }
    
  3. 创建 Context

    public class Context {
       private State state;
     
       public Context(){
          state = null;
       }
     
       public void setState(State state){
          this.state = state;     
       }
     
       public State getState(){
          return state;
       }
    }
    
  4. 使用 Context 来查看当状态 State 改变时的行为变化

    public class StatePatternDemo {
       public static void main(String[] args) {
          Context context = new Context();
     
          StartState startState = new StartState();
          startState.doAction(context);
     
          System.out.println(context.getState().toString());
     
          StopState stopState = new StopState();
          stopState.doAction(context);
     
          System.out.println(context.getState().toString());
       }
    }
    
  5. 执行程序,输出结果

    Player is in start state
    Start State
    Player is in stop state
    Stop State

优点

  1. 封装了转换规则
  2. 枚举可能的状态,在枚举状态之前需要确定状态种类
  3. 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为
  4. 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块
  5. 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数

缺点

  1. 状态模式的使用必然会增加系统类和对象的个数
  2. 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱
  3. 状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码

9. 访问者模式

在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法.通过这种方式,元素的执行算法可以随着访问者改变而改变.这种类型的设计模式属于行为型模式.根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作.

代码实现

  1. 定义一个表示元素的接口

    public interface ComputerPart {
       void accept(ComputerPartVisitor computerPartVisitor);
    }
    
  2. 创建扩展了上述类的实体类

    public class Keyboard implements ComputerPart {
     
       @Override
       public void accept(ComputerPartVisitor computerPartVisitor) {
          computerPartVisitor.visit(this);
       }
    }
    
    public class Monitor implements ComputerPart {
     
       @Override
       public void accept(ComputerPartVisitor computerPartVisitor) {
          computerPartVisitor.visit(this);
       }
    }
    
    public class Mouse implements ComputerPart {
     
       @Override
       public void accept(ComputerPartVisitor computerPartVisitor) {
          computerPartVisitor.visit(this);
       }
    }
    
    public class Computer implements ComputerPart {
       
       ComputerPart[] parts;
     
       public Computer(){
          parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()};      
       } 
     
     
       @Override
       public void accept(ComputerPartVisitor computerPartVisitor) {
          for (int i = 0; i < parts.length; i++) {
             parts[i].accept(computerPartVisitor);
          }
          computerPartVisitor.visit(this);
       }
    }
    
  3. 定义一个表示访问者的接口

    public interface ComputerPartVisitor {
       void visit(Computer computer);
       void visit(Mouse mouse);
       void visit(Keyboard keyboard);
       void visit(Monitor monitor);
    }
    
  4. 创建实现了上述类的实体访问者

    public class ComputerPartDisplayVisitor implements ComputerPartVisitor {
     
       @Override
       public void visit(Computer computer) {
          System.out.println("Displaying Computer.");
       }
     
       @Override
       public void visit(Mouse mouse) {
          System.out.println("Displaying Mouse.");
       }
     
       @Override
       public void visit(Keyboard keyboard) {
          System.out.println("Displaying Keyboard.");
       }
     
       @Override
       public void visit(Monitor monitor) {
          System.out.println("Displaying Monitor.");
       }
    }
    
  5. 使用 ComputerPartDisplayVisitor来显示 Computer 的组成部分

    public class VisitorPatternDemo {
       public static void main(String[] args) {
     
          ComputerPart computer = new Computer();
          computer.accept(new ComputerPartDisplayVisitor());
       }
    }
    
  6. 执行程序,输出结果

    Displaying Mouse.
    Displaying Keyboard.
    Displaying Monitor.
    Displaying Computer.

优点

  1. 符合单一职责原则
  2. 优秀的扩展性
  3. 灵活性

缺点

  1. 具体元素对访问者公布细节,违反了迪米特原则
  2. 具体元素变更比较困难
  3. 违反了依赖倒置原则,依赖了具体类,没有依赖抽象

10. 中介者模式

中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性.这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护.中介者模式属于行为型模式.

代码实现

  1. 创建中介类

    public class ChatRoom {
       public static void showMessage(User user, String message){
          System.out.println(new Date() + " [" + user.getName() +"] : " + message);
       }
    }
    
  2. 创建 user

    public class User {
       private String name;
     
       public String getName() {
          return name;
       }
     
       public void setName(String name) {
          this.name = name;
       }
     
       public User(String name){
          this.name  = name;
       }
     
       public void sendMessage(String message){
          ChatRoom.showMessage(this,message);
       }
    }
    
  3. 使用 User 对象来显示他们之间的通信

    public class MediatorPatternDemo {
       public static void main(String[] args) {
          User robert = new User("Robert");
          User john = new User("John");
     
          robert.sendMessage("Hi! John!");
          john.sendMessage("Hello! Robert!");
       }
    }
    
  4. 执行程序,输出结果

    Fri Aug 12 14:57:23 CST 2022 [Robert] : Hi! John!
    Fri Aug 12 14:57:23 CST 2022 [John] : Hello! Robert!

优点

  1. 降低了类的复杂度,将一对多转化成了一对一
  2. 各个类之间的解耦
  3. 符合迪米特原则

缺点

中介者会庞大,变得复杂难以维护

11. 解释器模式

解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式.这种模式实现了一个表达式接口,该接口解释一个特定的上下文.这种模式被用在 SQL 解析、符号处理引擎等.

代码实现

  1. 创建一个表达式接口

    public interface Expression {
       boolean interpret(String context);
    }
    
  2. 创建实现了上述接口的实体类

    public class TerminalExpression implements Expression {
       
       private String data;
     
       public TerminalExpression(String data){
          this.data = data; 
       }
     
       @Override
       public boolean interpret(String context) {
          if(context.contains(data)){
             return true;
          }
          return false;
       }
    }
    
    public class OrExpression implements Expression {
        
       private Expression expr1 = null;
       private Expression expr2 = null;
     
       public OrExpression(Expression expr1, Expression expr2) { 
          this.expr1 = expr1;
          this.expr2 = expr2;
       }
     
       @Override
       public boolean interpret(String context) {      
          return expr1.interpret(context) || expr2.interpret(context);
       }
    }
    
    public class AndExpression implements Expression {
        
       private Expression expr1 = null;
       private Expression expr2 = null;
     
       public AndExpression(Expression expr1, Expression expr2) { 
          this.expr1 = expr1;
          this.expr2 = expr2;
       }
     
       @Override
       public boolean interpret(String context) {      
          return expr1.interpret(context) && expr2.interpret(context);
       }
    }
    
  3. InterpreterPatternDemo 使用 Expression 类来创建规则,并解析它们

    public class InterpreterPatternDemo {
     
       //规则:Robert 和 John 是男性
       public static Expression getMaleExpression(){
          Expression robert = new TerminalExpression("Robert");
          Expression john = new TerminalExpression("John");
          return new OrExpression(robert, john);    
       }
     
       //规则:Julie 是一个已婚的女性
       public static Expression getMarriedWomanExpression(){
          Expression julie = new TerminalExpression("Julie");
          Expression married = new TerminalExpression("Married");
          return new AndExpression(julie, married);    
       }
     
       public static void main(String[] args) {
          Expression isMale = getMaleExpression();
          Expression isMarriedWoman = getMarriedWomanExpression();
     
          System.out.println("John is male? " + isMale.interpret("John"));
          System.out.println("Julie is a married women? " 
          + isMarriedWoman.interpret("Married Julie"));
       }
    }
    
  4. 执行程序,输出结果

    John is male? true
    Julie is a married women? true

优点

  1. 可扩展性好,灵活
  2. 增加了新的解释表达式的方式
  3. 易于实现简单方法

缺点

  1. 可利用场景较少

  2. 对于复杂的方法比较难维护

  3. 解释器模式会引起类膨胀

  4. 降低了类的复杂度,将一对多转化成了一对一

  5. 各个类之间的解耦

  6. 符合迪米特原则

缺点

中介者会庞大,变得复杂难以维护

11. 解释器模式

解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式.这种模式实现了一个表达式接口,该接口解释一个特定的上下文.这种模式被用在 SQL 解析、符号处理引擎等.

代码实现

  1. 创建一个表达式接口

    public interface Expression {
       boolean interpret(String context);
    }
    
  2. 创建实现了上述接口的实体类

    public class TerminalExpression implements Expression {
       
       private String data;
     
       public TerminalExpression(String data){
          this.data = data; 
       }
     
       @Override
       public boolean interpret(String context) {
          if(context.contains(data)){
             return true;
          }
          return false;
       }
    }
    
    public class OrExpression implements Expression {
        
       private Expression expr1 = null;
       private Expression expr2 = null;
     
       public OrExpression(Expression expr1, Expression expr2) { 
          this.expr1 = expr1;
          this.expr2 = expr2;
       }
     
       @Override
       public boolean interpret(String context) {      
          return expr1.interpret(context) || expr2.interpret(context);
       }
    }
    
    public class AndExpression implements Expression {
        
       private Expression expr1 = null;
       private Expression expr2 = null;
     
       public AndExpression(Expression expr1, Expression expr2) { 
          this.expr1 = expr1;
          this.expr2 = expr2;
       }
     
       @Override
       public boolean interpret(String context) {      
          return expr1.interpret(context) && expr2.interpret(context);
       }
    }
    
  3. InterpreterPatternDemo 使用 Expression 类来创建规则,并解析它们

    public class InterpreterPatternDemo {
     
       //规则:Robert 和 John 是男性
       public static Expression getMaleExpression(){
          Expression robert = new TerminalExpression("Robert");
          Expression john = new TerminalExpression("John");
          return new OrExpression(robert, john);    
       }
     
       //规则:Julie 是一个已婚的女性
       public static Expression getMarriedWomanExpression(){
          Expression julie = new TerminalExpression("Julie");
          Expression married = new TerminalExpression("Married");
          return new AndExpression(julie, married);    
       }
     
       public static void main(String[] args) {
          Expression isMale = getMaleExpression();
          Expression isMarriedWoman = getMarriedWomanExpression();
     
          System.out.println("John is male? " + isMale.interpret("John"));
          System.out.println("Julie is a married women? " 
          + isMarriedWoman.interpret("Married Julie"));
       }
    }
    
  4. 执行程序,输出结果

    John is male? true
    Julie is a married women? true

优点

  1. 可扩展性好,灵活
  2. 增加了新的解释表达式的方式
  3. 易于实现简单方法

缺点

  1. 可利用场景较少
  2. 对于复杂的方法比较难维护
  3. 解释器模式会引起类膨胀
  4. 解释器模式采用递归调用方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值