Design Pattern

1、设计模式简介

Gang of Four
(
GoF
)

的分类将设计模式分类为 23 种经典的模式,根据用途我们又可以分为三大类,分别为创建型模式、结构型模式和行为型模式。

重要的设计模式

1.面向接口编程,而不是面向实现。这个很重要,也是优雅的、可扩展的代码的第一步,这就不需要多说了吧。

2.职责单一原则。每个类都应该只有一个单一的功能,并且该功能应该由这个类完全封装起来。

3.对修改关闭,对扩展开放。对修改关闭是说,我们辛辛苦苦加班写出来的代码,该实现的功能和该修复的 bug 都完成了,别人可不能说改就改;对扩展开放就比较好理解了,也就是说在我们写好的代码基础上,很容易实现扩展。

2、设计模式类型

2.1 创建者模式

2.1.1 简单工厂模式

2.1.2 工厂模式

2.1.3 抽象工厂模式

2.1.4 单例模式(Singleton Pattern)

介绍

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

public class Singleton {
    // static保证只加载一次,不会出现循环的问题
    private static final Singleton singleton = new Singleton(); //限制产生多个对象
    private Singleton(){
    }
    //通过该方法获得实例对象
    public static Singleton getSingleton(){
    	return singleton;
    }
    //类中其他方法,尽量是 static
    public static void doSomething(){
    }
}

使用场景

  • 要求生成唯一序列号的环境;
  • 在整个项目中需要一个共享访问点或共享数据,例如一个 Web 页面上的计数 器,可以不用把每次刷新都记录到数据库中,使用单例模式保持计数器的值,并确 保是线程安全的;
  • 创建一个对象需要消耗的资源过多,如要访问 IO 和数据库等资源;
  • 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式 (当然,也可以直接声明为 static 的方式)。

具体场景

  • Artop开发的时候Explorer的Activator就是一个单例。

为什么Singleton类内实例化自己不会循环?

因为static保证只加载一次(final变量、static变量等线程共用的东西存放在JVM方法区,只加载一次),不会出现循环的问题。

2.1.5建造者模式

2.1.6 原型模式

2.1.7 创建型模式总结

2.2 结构性模式

2.2.1 代理模式

2.2.2 适配器模式

介绍

功能:有一个接口需要实现,但是我们现成的对象都不满足,需要加一层适配器来进行适配。适配器模式具体可分为,默认适配器模式、对象适配器模式、类适配器模式。

  • 默认适配器模式(Default Adapter)

    // FileAlterationListener接口中定义了众多方法
    public interface FileAlterationListener {
        void onStart(final FileAlterationObserver observer);
        void onDirectoryCreate(final File directory);
        void onDirectoryChange(final File directory);
        void onDirectoryDelete(final File directory);
        void onFileCreate(final File file);
        void onFileChange(final File file);
        void onFileDelete(final File file);
        void onStop(final FileAlterationObserver observer);
    }
    

    而有时希望实现其中几个方法,而不是全部实现,但是接口的方法必须实现,比较麻烦。可以通过一个适配器将不需要的方法统一设置为空方法,这样所有继承适配器的方法可以单独实现某几个类。

    public class FileAlterationListenerAdaptor implements FileAlterationListener {
        public void onStart(final FileAlterationObserver observer) {    }
        public void onDirectoryCreate(final File directory) {    }
        public void onDirectoryChange(final File directory) {    }
        public void onDirectoryDelete(final File directory) {    }
        public void onFileCreate(final File file) {    }
        public void onFileChange(final File file) {    }
        public void onFileDelete(final File file) {    }
        public void onStop(final FileAlterationObserver observer) {    }
    }
    

    接着继承该方法,即可做到只实现最初接口中的某几个方法,

    public class FileMonitor extends FileAlterationListenerAdaptor {
        public void onFileCreate(final File file) {
            // 文件创建
            doSomething();
        }
        public void onFileDelete(final File file) {
            // 文件删除
            doSomething();
        }
    }
    

具体场景

  • Spring Security在配置资源安全和http安全时,分别有适配器——ResourceServerConfigurerAdapterAuthorizationServerConfigurerAdapter

        @Configuration
        @EnableResourceServer
        protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
            @Override
            public void configure(ResourceServerSecurityConfigurer resources) {
                resources.resourceId(DEMO_RESOURCE_ID).stateless(true);
            }
            @Override
            public void configure(HttpSecurity http) throws Exception {
                http
                        .authorizeRequests()
                        .antMatchers("/order/**").authenticated();//配置order访问控制,必须认证过后才可以访问
            }
        }
    

2.2.3 桥梁模式

2.2.4 装饰模式

2.2.5 门面模式

2.2.6 组合模式(Composite Pattern)

介绍

组合模式用于表示具有层次结构的数据,使得我们对单个对象和组合对象的访问具有一致性。例如,

每个员工都有姓名、部门、薪水这些属性,同时还有下属员工集合(虽然可能集合为空),而下属员工和自己的结构是一样的,也有姓名、部门这些属性,同时也有他们的下属员工集合。

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

   public String toString(){
      return ("Employee :[ Name : " + name + ", dept : " + dept + ", salary :" + salary+" ]");
   }   
}

应用场景

  • Eclipse界面开发SWT的容器父类,可以包含任意多的基本控件或者子容器控件。

2.2.7 享元模式

2.2.8 结构性模式总结

2.3 行为型模式

2.3.1 策略模式

2.3.2 观察者模式(Observe Pattern)

介绍

观察者模式包括两个操作

  • 观察者订阅自己关心的主题
  • 主题有数据变化后通知观察者

实现过程如下:

1.定义主题,每个主题持有观察者列表的引用,用在数据变更的时候通知各个观察者

// 主题
public class Subject {
	// 观察者列表
    // Observer是一个接口,可以在此处存入多个Observer接口的应用类
   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.定义观察者接口

public abstract class Observer {
   protected Subject subject;
   public abstract void update();
}

其实如果只有一个观察者类的话,接口都不用定义了,不过,通常场景下,既然用到了观察者模式,我们就是希望一个事件出来了,会有多个不同的类需要处理相应的信息。比如,订单修改成功事件,我们希望发短信的类得到通知、发邮件的类得到通知、处理物流信息的类得到通知等。

3.定义观察者类

下面定义2个观察者

// Binary观察者
public class BinaryObserver extends Observer {
      // 在构造方法中进行订阅主题
    public BinaryObserver(Subject subject) {
        this.subject = subject;
        // 通常在构造方法中将 this 发布出去的操作一定要小心
        this.subject.attach(this);
    }
      // 该方法由主题类在数据变更的时候进行调用
    @Override
    public void update() {
        String result = Integer.toBinaryString(subject.getState());
        System.out.println("订阅的数据发生变化,新的数据处理为二进制值为:" + result);
    }
}
// Hexa观察者
public class HexaObserver extends Observer {

    public HexaObserver(Subject subject) {
        this.subject = subject;
        this.subject.attach(this);
    }

    @Override
    public void update() {
          String result = Integer.toHexString(subject.getState()).toUpperCase();
        System.out.println("订阅的数据发生变化,新的数据处理为十六进制值为:" + result);
    }
}

4.客户端

public static void main(String[] args) {
    // 先定义一个主题
      Subject subject1 = new Subject();
      // 定义观察者
      new BinaryObserver(subject1);
      new HexaObserver(subject1);

      // 模拟数据变更,这个时候,观察者们的 update 方法将会被调用
      subject.setState(11);
}

输出

订阅的数据发生变化,新的数据处理为二进制值为:1011
订阅的数据发生变化,新的数据处理为十六进制值为:B

jdk 也提供了相似的支持,具体参考 java.util.Observable java.util.Observer 这两个类。

实际生产过程中,观察者模式往往用消息中间件来实现,如果要实现单机观察者模式,建议读者使用 Guava 中的 EventBus,它有同步实现也有异步实现,本文主要介绍设计模式,就不展开说了。

使用场景

  • 消息中间件,实现消息订阅,如Kafka
  • Eclipse SWT的事件处理机制,事件发送者声明监听器接口,对事件感兴趣的各方实现此接口并将监听器注册到事件的发送者上

2.3.3 责任链模式

2.3.4 模板方法模式

2.3.5 状态模式

2.3.6 行为型模式总结

2.2 工厂模式

定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类 的实例化延迟到其子类。

具体的工厂模式代码如下,product 为抽象产品类负责定义产品的共性,实现对事物最抽象的定义; Creator 为抽象创建类,也就是抽象工厂,具体如何创建产品类是由具体的实现工 厂 ConcreteCreator 完成的。

public class ConcreteCreator extends Creator {
    public T createProduct(Class c){
        Product product=null;
        try {
            //通过Class.forname产生一个新的对象
            product =(Product)Class.forName(c.getName()).newInstance();
        } catch (Exception e) {
            //异常处理
        }
        return (T)product;
    }
}

简单工厂模式

一个模块仅需要一个工厂类,没有必要把它产生出来,使用静态的方法

多个工厂类

每个人种(具体的产品类)都对应了一个创建者,每个创建者独立负责创建对应的 产品对象,非常符合单一职责原则

代替单例模式

单例模式的核心要求就是在内存中只有一个对象,通过工厂方法模式也可以只在内 存中生产一个对象

延迟初始化

ProductFactory 负责产品类对象的创建工作,并且通过prMap变量产生一个缓存,对需要再次被重用的对象保留

使用场景

jdbc 连接数据库,硬件访问,降低对象的产生和销毁

2.3 抽象工厂模式

为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类

public abstract class AbstractCreator {
	//创建 A 产品家族
	public abstract AbstractProductA createProductA(); 
    //创建 B 产品家族
	public abstract AbstractProductB createProductB(); 
}

使用场景

一个对象族(或是一组没有任何关系的对象)都有相同的约束。 涉及不同操作系统的时候,都可以考虑使用抽象工厂模式

2.4 模板方法模式(Template Method Pattern)

定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义 该算法的某些特定步骤

2.4.1 抽象模板

AbstractClass 叫做抽象模板,分为具体方法和模板方法,

具体方法

基本方法也叫做基本操作,是由子类实现的方法,并且在模板方法被调用。

模板方法

可以有一个或几个,一般是一个具体方法,也就是一个框架,实现对基本方法的调度,完成固定的逻辑。

注意:为了防止恶意的操作,一般模板方法都加上 final 关键字,不允许被覆写。

2.4.2 具体模板

ConcreteClass1 和ConcreteClass2 属于具体模板,实现父类所定义的 一个或多个抽象方法,也就是父类定义的基本方法在子类中得以实现

使用场景

● 多个子类有公有的方法,并且逻辑基本相同时。

● 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由 各个子类实现。

● 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然 后通过钩子函数(见“模板方法模式的扩展”)约束其行为

2.5 MVC模式

MVC:Model(模型)、View(视图)和Controller(控制)

1)最上面的一层,是直接面向最终用户的”视图层”(View)。它是提供给用户的操作界面,是程序的外壳。

2)最底下的一层,是核心的”数据层”(Model),也就是程序需要操作的数据或信息。

3)中间的一层,就是”控制层”(Controller),它负责根据用户从”视图层”输入的指令,选取”数据层”中的数据,然后对其进行相应的操作,产生最终结果。


欢迎关注我的微信公众号

互联网矿工

funpeefun

Search

    Post Directory