设计模式丶

设计模式的六大原则

1、开闭原则(Open Close Principle)

开闭原则的意思是:对扩展开放,对修改关闭

2、里氏代换原则(Liskov Substitution Principle)

任何基类可以出现的地方,子类一定可以出现。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

3、依赖倒转原则(Dependence Inversion Principle)

这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。

4、接口隔离原则(Interface Segregation Principle)

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。

5、迪米特法则,又称最少知道原则(Demeter Principle)

最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。

6、合成复用原则(Composite Reuse Principle)

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

单例模式 Singleton Pattern

饿汉式单例

//饿汉式单例
public class SingletonHungry {
    //会浪费空间,所以应该是需要用得时候才创建Hungry对象 即用懒汉模式
    private byte[] data1 = new byte[1024*1024];
    private byte[] data2 = new byte[1024*1024];
    private byte[] data3 = new byte[1024*1024];
    private byte[] data4 = new byte[1024*1024];

    private SingletonHungry() {
    }

    private final static SingletonHungry HUNGRY = new SingletonHungry();

    public static SingletonHungry getInstance() {
        return HUNGRY;
    }
}

饱汉式单例(synchronized线程安全)

/**
 * 饱汉模式
 */
public class SingletonFull {

    private static SingletonFull instance = null;

    private SingletonFull() {
    }

    public static synchronized SingletonFull getInstance(){
        if(null==instance)
            instance = new SingletonFull();
        return instance;
    }
}

饱汉式单例,双重检测锁(线程安全)

/**
 * 饱汉模式的双重锁模式,提高效率
 */
public class SingletonDouble {
   private static SingletonDouble singleton;
   
   private SingletonDouble(){
   }
   
   public static SingletonDouble getInstance(){
      if(singleton == null){
         synchronized(SingletonDouble.class){
            if(singleton == null){
               singleton = new SingletonDouble();
            }
         }
      }
      return singleton;
   }
}

静态内部类玩法(线程安全)

/**
 * 静态内部类玩法
 */
public class Holder {
    private Holder() {
    }

    public static Holder getInstance() {
        return InnerClass.HOLDER;
    }

    public static class InnerClass{
        private static final Holder HOLDER = new Holder();
    }
}

DCL懒(饱)汉式(双重检测锁+volatile禁止指令重排)(也可能被反射破坏)

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

/**
 * DCL懒(饱)汉式(双重检测锁+volatile禁止指令重排)(也可能被反射破坏)
 */
public class LazyMan {
    /*public LazyMan() {
        System.out.println(Thread.currentThread().getName()+" ok");
    }*/

    private static boolean xiaoming = false;

    public LazyMan() {
        synchronized (LazyMan.class) {
            /*
            if (lazyMan != null) {//第三重检测
                throw new RuntimeException("不要试图使用反射破坏单例");
            }
            */
            if (!xiaoming){
                xiaoming = true;
            }else{
                throw new RuntimeException("不要试图使用反射破坏单例");
            }
        }
    }

    /*
    private static LazyMan lazyMan;
    //单线程下单例ok  多线程下有问题
    public static LazyMan getInstance() {
        if(null ==lazyMan){
            lazyMan = new LazyMan();
        }
        return lazyMan;
    }*/

    private volatile static LazyMan lazyMan;//volatile
    //双重检测锁模式+volatile禁止指令重排 DCL懒汉!!!!!!!!!!!!!!!!
    public static LazyMan getInstance() {
        if(null ==lazyMan){
            synchronized (LazyMan.class) {
                if(null ==lazyMan){
                    lazyMan = new LazyMan();//不是原子性操作,极端情况下也是有问题得
                    /**
                     * 1分配内存空间
                     * 2执行构造方法,初始化对象
                     * 3把对象指向这个空间
                     * 可能因指令重排产生问题
                     */
                }
            }
        }
        return lazyMan;
    }

    public static void main(String[] args) throws Exception {
        /*
        //测试
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                LazyMan.getInstance();
            }).start();
        }*/

        //反射破坏单例模式
        //LazyMan instance = LazyMan.getInstance();

        Field xiaoming = LazyMan.class.getDeclaredField("xiaoming");
        xiaoming.setAccessible(true);

        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);

        LazyMan instance = declaredConstructor.newInstance();
        xiaoming.set(instance,false);
        LazyMan instance2 = declaredConstructor.newInstance();

        System.out.println(instance.hashCode());
        System.out.println(instance2.hashCode());
    }
}

枚举避免反射破坏单例模式

import java.lang.reflect.Constructor;

/**
 * 枚举避免破坏单例模式
 * enum 本身也是一个class类
 */
public enum EnumSingle {

    INSTANCE;

    public EnumSingle getInstance() {
        return INSTANCE;
    }
}

class Test {
    //Cannot reflectively create enum objects 结论:枚举可以防止反射破坏单例
    public static void main(String[] args) throws Exception {
//        EnumSingle instance1 = EnumSingle.INSTANCE;
//        EnumSingle instance2 = EnumSingle.INSTANCE;
//        System.out.println(instance1);
//        System.out.println(instance2);

        EnumSingle instance1 = EnumSingle.INSTANCE;
        //Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);
        declaredConstructor.setAccessible(true);
        EnumSingle instance2 = declaredConstructor.newInstance();
        //NoSuchMethodException
        System.out.println(instance1);
        System.out.println(instance2);
    }
}

模板模式 Template Pattern

抽象父类

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 先在抽象父类中写一个模板方法(功能增强),具体的业务写在另一个抽象方法中,供子类重写
 */
public abstract class BaseServlet extends HttpServlet {

    public final void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        //记录访问日志
        //进行权限判断
        if(true){//具有权限
            try{
                doService(request,response);
            }catch(Exception e){
                //记录异常信息
            }
        }
    }

    //protected 给子孙用
    protected abstract void doService(HttpServletRequest request,HttpServletResponse response) throws IOException,ServletException;
}

子类

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class MyServlet1 extends BaseServlet {

    @Override
    protected void doService(HttpServletRequest request,HttpServletResponse response) throws IOException, ServletException {
        //本Servlet只处理具体的业务逻辑代码
    }
}

代理模式 Proxy Pattern

记忆点:涉及到一个背景:改动原有代码是公司中的大忌,用代理模式轻松绕开

最佳实践:房屋中介

静态代理

  • 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
  • 公共业务交给代理角色,实现了业务的分工
  • 公共业务需要扩展的时候,方便集中管理
  • 缺点是:一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会变低

例一

//租房(抽象角色:一般使用接口或者抽象类)
public interface Rent {
    public void rent();
}
//房东(真实角色:被代理的角色)
public class Host implements Rent {
    public void rent() {
        System.out.println("本房东要出租房子了");
    }
}
//租客(访问代理对象的人)
public class Client {
    public static void main(String[] args) {
        Host host = new Host();
        //host.rent();//如果我们能直接联系到房东,就可以不通过中介
        //通过代理的方式,找专业的房屋中介
        Proxy proxy = new Proxy(host);
        proxy.rent();
    }
}
//中介(代理角色:代理真实角色,通常会做一些附加操作)
public class Proxy implements Rent {

    private Host host;

    public Proxy() {
    }

    public Proxy(Host host) {//或使用set方式注入
        this.host = host;
    }

    public void rent() {
        hetong();
        host.rent();
        jiaoqian();
    }

    public void hetong() {
        System.out.println("链家:你们两方签合同");
    }

    public void jiaoqian() {
        System.out.println("链家:这边把费用算一下");
    }
}

例二

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}
public class UserServiceImpl implements UserService{
    public void add() {
        System.out.println("增加用户");
    }

    public void delete() {
        System.out.println("删除用户");
    }

    public void update() {
        System.out.println("更新用户");
    }

    public void query() {
        System.out.println("查询用户");
    }
}
public class Client {
    public static void main(String[] args) {
        UserServiceImpl service = new UserServiceImpl();
        //service.add();//不使用代理时
        UserServiceProxy proxy = new UserServiceProxy();
        proxy.setUserService(service);
        proxy.add();
    }
}
public class UserServiceProxy implements UserService {

    private UserServiceImpl userService;

    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }

    public void add() {
        log("add");
        userService.add();
    }

    public void delete() {
        log("delete");
        userService.delete();
    }

    public void update() {
        log("update");
        userService.update();
    }

    public void query() {
        log("query");
        userService.query();
    }

    public void log(String msg){
        System.out.println("[debug]使用了"+msg+"方法");
    }
}

动态代理

例一

//租房
public interface Rent {
    public void rent();
}
//房东
public class Host implements Rent {
    public void rent() {
        System.out.println("本房东要出租房子了");
    }
}
public class Client {
    public static void main(String[] args) {
        //真实角色
        Host host = new Host();
        //创建代理角色
        ProxyInvocationHandler handler = new ProxyInvocationHandler();
        handler.setRent(host);
        Rent proxy = (Rent) handler.getProxy();
        proxy.rent();
    }
}
//用这个类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口(写成Object就通用了)
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //生成得到代理类
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
    }

    //处理代理实例 并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        hetong();
        //动态代理的本质就是使用反射机制
        Object object = method.invoke(rent, args);
        jiaoqian();
        return object;
    }

    public void hetong() {
        System.out.println("链家:你们两方签合同");
    }

    public void jiaoqian() {
        System.out.println("链家:这边把费用算一下");
    }
}

例二

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}
public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("增加用户");
    }

    public void delete() {
        System.out.println("删除用户");
    }

    public void update() {
        System.out.println("更新用户");
    }

    public void query() {
        System.out.println("查询用户");
    }
}
public class Client {
    public static void main(String[] args) {
        //真实角色
        UserServiceImpl userService = new UserServiceImpl();
        //代理角色
        ProxyInvocationHandler handler = new ProxyInvocationHandler();
        handler.setTarget(userService);//设置要代理的对象
        //动态生成代理类
        UserService proxy = (UserService) handler.getProxy();
        proxy.add();
    }
}
//用这个类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    //生成得到代理类
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    //处理代理实例 并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());
        //动态代理的本质就是使用反射机制
        Object object = method.invoke(target, args);
        return object;
    }

    public void log(String msg){
        System.out.println("[debug]使用了"+msg+"方法");
    }
}

工厂模式 Factory Pattern

创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象

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

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

何时使用:我们明确地计划不同条件下创建不同实例时。

如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。

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

应用实例: 1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。 2、Hibernate 换数据库只需换方言和驱动就可以。

优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。

缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

使用场景: 1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。

注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

用例(反映思想)

//画形状的接口
public interface Shape {
   void draw();
}
//画圆
public class Circle implements Shape {
   @Override
   public void draw() {
      System.out.println("Inside Circle::draw() method.");
   }
}
//画矩形
public class Rectangle implements Shape {
   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}
//正方形
public class Square implements Shape {
 
   @Override
   public void draw() {
      System.out.println("Inside Square::draw() method.");
   }
}
//工厂类
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;
   }
}
//使用
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();
   }
}

上例的基础上,用枚举实现工厂

public enum Factory {
    CIRCLE(new Circle(),"CIRCLE"),
    RECTANGLE(new Rectangle(),"RECTANGLE"),
    SQUARE(new Square(),"SQUARE");
    
    // 成员变量  
    private Shape shape;
    private String name;  
    
    // 普通方法  
    public static Shape getShape(String name) {  
        for (Factory c : Factory.values()) {  
            if (c.name == name) {  
                return c.shape;  
            }  
        }  
        return null;  
    } 
    // 构造方法  
    private Factory(Shape shape, String name) {  
        this.shape = shape;  
        this.name = name;  
    } 
    public String getName() {
        return name;
    }
    public Shape getShape() {
        return shape;
    }

    public void setShape(Shape shape) {
        this.shape = shape;
    }

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

public class Test {
    public static void main(String[] args) {
        /*使用枚举类*/
        Factory.getShape("CIRCLE").draw();
        Factory.getShape("RECTANGLE").draw();
        Factory.getShape("SQUARE").draw();
    }
}
  • 简单工厂:一个工厂类,一个产品抽象类。(简单工厂模式不是 23 种里的一种)

  • 工厂方法:多个工厂类,一个产品抽象类。

  • 抽象工厂:多个工厂类,多个产品抽象类。

  • 简单工厂类:一个麦当劳店,可以生产多种汉堡。

  • 工厂方法类:一个麦当劳店,可以生产多种汉堡。一个肯德基店,也可以生产多种汉堡。

  • 抽象工厂类:百胜餐饮集团下有肯德基和百事公司,肯德基生产汉堡,百事公司生成百事可乐。

抽象工厂模式 Abstract Factory Pattern

工厂的工厂。抽象工厂就意味着这个工厂是拿来继承的,它是一个概念,它可以实例化很多个工厂。

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

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

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

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

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

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

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

使用场景: 1、QQ 换皮肤,一整套一起换。 2、生成不同操作系统的程序。

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

使用栗子

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

全部源码见文末链接

建造者模式 Builder Pattern

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

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

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

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

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

应用实例: 1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。 2、JAVA 中的 StringBuilder。

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

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

使用场景: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。

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

Bottle

原型模式 Prototype Pattern

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

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

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

何时使用: 1、当一个系统应该独立于它的产品创建,构成和表示时。 2、当要实例化的类是在运行时刻指定时,例如,通过动态装载。 3、为了避免创建一个与产品类层次平行的工厂类层次时。 4、当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。

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

关键代码: 1、实现克隆操作,在 JAVA 继承 Cloneable,重写 clone()。2、原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些"易变类"拥有稳定的接口。

应用实例: 1、细胞分裂。 2、JAVA 中的 Object clone() 方法。

优点: 1、性能提高。 2、逃避构造函数的约束。

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

使用场景: 1、资源优化场景。 2、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。 3、性能和安全要求的场景。 4、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。 5、一个对象多个修改者的场景。 6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。 7、在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。

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

PrototypePatternDemo

适配器模式 Adapter Pattern

两个不兼容的接口之间的桥梁

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

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

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

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

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

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

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

缺点: 1、过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。 2.由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。

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

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

AudioPlayer

桥接 Bridge Pattern

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

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

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

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

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

应用实例: 1、猪八戒从天蓬元帅转世投胎到猪,转世投胎的机制将尘世划分为两个等级,即:灵魂和肉体,前者相当于抽象化,后者相当于实现化。生灵通过功能的委派,调用肉体对象的功能,使得生灵可以动态地选择。 2、墙上的开关,可以看到的开关是抽象的,不用管里面具体怎么实现的。

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

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

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

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

BridgePatternDemo

过滤器模式 Filter Pattern

或标准模式 Criteria Pattern。使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来

CriteriaPatternDemo

组合模式 Composite Pattern

用于把一组相似的对象当作一个单一的对象

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

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

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

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

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

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

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

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

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

注意事项:定义时为具体类。

Employee

装饰器模式 Decorator Pattern

向一个现有的对象添加新的功能,同时又不改变其结构

意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

主要解决:扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

何时使用:在不想增加很多子类的情况下扩展类。

如何解决:将具体功能职责划分,同时继承装饰者模式。

关键代码: 1、Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。

应用实例: 1、孙悟空有 72 变,当他变成"庙宇"后,他的根本还是一只猴子,但是他又有了庙宇的功能。 2、不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。

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

缺点:多层装饰比较复杂。

使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。

注意事项:可代替继承。

DecoratorPatternDemo

参考教程|本篇用例源码

posted @ 2020-11-26 23:48  夜雨秋池  阅读(78)  评论(0编辑  收藏  举报