设计模式
设计模式要活学活用,不要生搬硬套,目的是让程序低耦合、高复用、高内聚、易扩展、易维护。
设计模式的类型
序号 | 模式 & 描述 | 包括 |
---|---|---|
1 | 创建型模式 这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。 | 工厂模式(Factory Pattern)抽象工厂模式(Abstract Factory Pattern)单例模式(Singleton Pattern)建造者模式(Builder Pattern)原型模式(Prototype Pattern) |
2 | 结构型模式 这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。 | 适配器模式(Adapter Pattern)桥接模式(Bridge Pattern)过滤器模式(Filter、Criteria Pattern)组合模式(Composite Pattern)装饰器模式(Decorator Pattern)外观模式(Facade Pattern)享元模式(Flyweight Pattern)代理模式(Proxy Pattern) |
3 | 行为型模式 这些设计模式特别关注对象之间的通信。 | 责任链模式(Chain of Responsibility Pattern)命令模式(Command Pattern)解释器模式(Interpreter Pattern)迭代器模式(Iterator Pattern)中介者模式(Mediator Pattern)备忘录模式(Memento Pattern)观察者模式(Observer Pattern)状态模式(State Pattern)空对象模式(Null Object Pattern)策略模式(Strategy Pattern)模板模式(Template Pattern)访问者模式(Visitor Pattern) |
4 | J2EE 模式 这些设计模式特别关注表示层。这些模式是由 Sun Java Center 鉴定的。 | MVC 模式(MVC Pattern)业务代表模式(Business Delegate Pattern)组合实体模式(Composite Entity Pattern)数据访问对象模式(Data Access Object Pattern)前端控制器模式(Front Controller Pattern)拦截过滤器模式(Intercepting Filter Pattern)服务定位器模式(Service Locator Pattern)传输对象模式(Transfer Object Pattern) |
设计模式的六大原则
1、开闭原则(Open Close Principle)
开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、里氏代换原则(Liskov Substitution Principle)
里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
通俗将就是子类可以扩展父类的功能,但不能改变父类原有的功能
3、依赖倒转原则(Dependence Inversion Principle)
这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
以此降低类与类之间的耦合性
4、接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。
接口可以多实现
5、迪米特法则,又称最少知道原则(Demeter Principle)
最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则(Composite Reuse Principle)
合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。
设计原则 | 一句话归纳 | 目的 |
---|---|---|
开闭原则 | 对扩展开放,对修改关闭 | 降低维护带来的新风险 |
依赖倒置原则 | 高层不应该依赖低层,要面向接口编程 | 更利于代码结构的升级扩展 |
单一职责原则 | 一个类只干一件事,实现类要单一 | 便于理解,提高代码的可读性 |
接口隔离原则 | 一个接口只干一件事,接口要精简单一 | 功能解耦,高聚合、低耦合 |
迪米特法则 | 不该知道的不要知道,一个类应该保持对其它对象最少的了解,降低耦合度 | 只和朋友交流,不和陌生人说话,减少代码臃肿 |
里氏替换原则 | 不要破坏继承体系,子类重写方法功能发生改变,不应该影响父类方法的含义 | 防止继承泛滥 |
合成复用原则 | 尽量使用组合或者聚合关系实现代码复用,少使用继承 | 降低代码耦合 |
23种设计模式
单例模式
某个类只能生成一个实例(该类能自行创建这个实例),该类提供了一个全局访问点公外部获取该实例,其拓展是有限多例模式
诸如windows中的任务管理器、回收站,操作系统中的文件系统,多线程中的线程池,显卡的驱动程序对象,打印机的后台处理服务,应用程序的日志对象,数据库的连接池,网站的计数器,web应用的配置对象,应用程序中的对话框,系统中的缓存等常常被设计成单例,J2EE标准中的ServletContext和ServletContextConfig,Spring框架应用中的ApplicationContext,目的是避免打开多个窗口而造成内存资源的浪费,或出现各个创个显示内容的不一致等错误。
单例模式的优缺点
优点
- 单例模式可以保证内存里只有一个实例,减少了内存的开销
- 可以避免对资源的多重占用
-
缺点
单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
- 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象
单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则
应用场景
对于Java来说,单例模式可以保证在一个jvm中值存在单一实例。应用场景:
需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少GC
- 某类只要求生成一个对象的时候,如一个班中的班长,每个人的身份证号等
- 某些类常见实例时占用资源较多,或实例化耗时长,且经常使用
- 某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池,网络连接池等
- 频繁访问数据库或文件的对象
- 对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,如果有多个实例,则系统会完全乱套
当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如web中的配置对象、数据库的连接池等
实现
单例模式是设计模式中最简单的模式之一。通常,普通类的构造函数是公有的,外部类可以通过new 构造函数()的方式来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外部提供一个静态的公有函数用于创建获取该静态私有实例。
懒汉式单例
该模式的特点是类加载时没有生成单例,只有当第一次调用getInstance方法时采取创建这个单例。
public class LazySingleton {
private static volatile LazySingleton instance = null; //保证 instance 在所有线程中同步
private LazySingleton() {
} //private 避免类在外部被实例化
public static synchronized LazySingleton getInstance() {
//getInstance 方法前加同步
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
注意:如果编写的是多线程程序,则不要删除上例代码中的关键字 volatile 和 synchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。 ```java public class SingletonLazy { public static void main(String[] args) {
President zt1 = President.getInstance();
zt1.getName(); //输出总统的名字
President zt2 = President.getInstance();
zt2.getName(); //输出总统的名字
if (zt1 == zt2) {
System.out.println("他们是同一人!");
} else {
System.out.println("他们不是同一人!");
}
} }
class President { private static volatile President instance = null; //保证instance在所有线程中同步
//private避免类在外部被实例化
private President() {
System.out.println("产生一个总统!");
}
public static synchronized President getInstance() {
//在getInstance方法上加同步
if (instance == null) {
instance = new President();
} else {
System.out.println("已经有一个总统,不能产生新总统!");
}
return instance;
}
public void getName() {
System.out.println("我是美国总统:特朗普。");
}
}
<a name="lVDoA"></a>
##### 饿汉式单例
该模式的特点是类一旦加载就创建一个实例,保证在调用getInstance方法之前单例就已经存在了
```java
public class HungrySingleton {
private static final HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return instance;
}
}
饿汉式单例在类创建的同时就已经创建好了一个静态的对象供系统使用,以后不会再改变,所以是线程安全的,可以直接用于多线程环境下而不会出现问题
import java.awt.*;
import javax.swing.*;
public class SingletonEager {
public static void main(String[] args) {
JFrame jf = new JFrame("饿汉单例模式测试");
jf.setLayout(new GridLayout(1, 2));
Container contentPane = jf.getContentPane();
Bajie obj1 = Bajie.getInstance();
contentPane.add(obj1);
Bajie obj2 = Bajie.getInstance();
contentPane.add(obj2);
if (obj1 == obj2) {
System.out.println("他们是同一人!");
} else {
System.out.println("他们不是同一人!");
}
jf.pack();
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
class Bajie extends JPanel {
private static Bajie instance = new Bajie();
private Bajie() {
JLabel l1 = new JLabel(new ImageIcon("src/Bajie.jpg"));
this.add(l1);
}
public static Bajie getInstance() {
return instance;
}
}
原型模式
将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。在这里,原型实例指定了要创建的对象的种类,用这种方式创建对象非常高效,根本无须知道对象创建的细节。例如,windows操作系统的安装通常较耗时间,如果复制就快了很多。
原型模式的优缺点
优点
- java自带的原型模式基于内存二进制流的复制,在性能上比直接new一个对象更加优良
可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,一遍在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
缺点
需要为每一个类都配置一个clone方法
- clone方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则
当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
应用场景
对象之间相同或相似,即只是个别的几个属性不同的时候
- 创建对象成本较大,例如初始化时间长,占用cpu太多,或者占用网络资源太多等,需要优化资源
- 创建一个对象需要繁琐的数据准备或访问权限等,需要提高性能或者提高安全性
- 系统中大量使用该类对象,且各个调用者都需要给它的属性重新赋值
在Spring中,原型模式应用的非常广泛。例如scope=’prototype’、JSON.parseObject()等都是原型模式的具体应用
实现
由于Java提供了对象的clone()方法,所以用java实现原型模式很简单
原型模式的克隆分为浅克隆和深克隆:浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址
- 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址
java中的Object类提供了浅克隆的clone()方法,具体原型类只要实现Cloneable接口就可以实现对象的浅克隆
//具体原型类
class Realizetype implements Cloneable {
Realizetype() {
System.out.println("具体原型创建成功!");
}
public Object clone() throws CloneNotSupportedException {
System.out.println("具体原型复制成功!");
return (Realizetype) super.clone();
}
}
//原型模式的测试类
public class PrototypeTest {
public static void main(String[] args) throws CloneNotSupportedException {
Realizetype obj1 = new Realizetype();
Realizetype obj2 = (Realizetype) obj1.clone();
System.out.println("obj1==obj2?" + (obj1 == obj2));
}
}
import java.awt.*;
import javax.swing.*;
class SunWukong extends JPanel implements Cloneable {
private static final long serialVersionUID = 5543049531872119328L;
public SunWukong() {
JLabel l1 = new JLabel(new ImageIcon("src/Wukong.jpg"));
this.add(l1);
}
public Object clone() {
SunWukong w = null;
try {
w = (SunWukong) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("拷贝悟空失败!");
}
return w;
}
}
public class ProtoTypeWukong {
public static void main(String[] args) {
JFrame jf = new JFrame("原型模式测试");
jf.setLayout(new GridLayout(1, 2));
Container contentPane = jf.getContentPane();
SunWukong obj1 = new SunWukong();
contentPane.add(obj1);
SunWukong obj2 = (SunWukong) obj1.clone();
contentPane.add(obj2);
jf.pack();
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
工厂模式
在日常开发中,凡是需要生成复杂对象(构造函数的参数过多)的地方,都可以尝试考虑使用工厂模式来代替
工厂模式的定义:定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中要求的“创建与使用相分离”的特点
按实际业务场景划分,工厂模式可分为简单工厂模式、工厂方法模式和抽象工厂模式。其中简单工厂模式不在23种设计模式的划分中。
把被创建的对象称为产品,把创建产品的对象称为工厂。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫做简单工厂模式
简单工厂模式
在简单工厂模式中创建实例的方法通常称为静态方法,因此简单工厂模式又叫做静态工厂方法模式
简单工厂模式每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这就增加了系统的复杂度,违背了开闭原则。而工厂方法模式就是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,这样就满足了开闭原则
应用场景
对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品
优缺点
优点:
- 工厂类包含必要的逻辑判断,可以决定在上面时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确
- 客户端无需知道锁创建具体产品的类名,只需要知道参数即可。
- 也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类
缺点:
- 简单工厂模式的工厂类单一,复制所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则
- 使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度
- 系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂
简单工厂模式使用了static工厂方法,造成工厂角色无法形成基于继承的等级结构
实现
public class Client {
public static void main(String[] args) {
}
//抽象产品
public interface Product {
void show();
}
//具体产品:ProductA
static class ConcreteProduct1 implements Product {
public void show() {
System.out.println("具体产品1显示...");
}
}
//具体产品:ProductB
static class ConcreteProduct2 implements Product {
public void show() {
System.out.println("具体产品2显示...");
}
}
final class Const {
static final int PRODUCT_A = 0;
static final int PRODUCT_B = 1;
static final int PRODUCT_C = 2;
}
static class SimpleFactory {
public static Product makeProduct(int kind) {
switch (kind) {
case Const.PRODUCT_A:
return new ConcreteProduct1();
case Const.PRODUCT_B:
return new ConcreteProduct2();
}
return null;
}
}
}
工厂方法模式
应用场景
客户只知道创建产品的工厂名,而不知道具体的产品名。如TCL点事工厂、海信电视工厂等
- 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只需要提供创建产品的接口
-
优缺点
优点:
用户只需要知道具体工厂的名称就可以得到所要的产品,无需知道产品的具体创建过程
- 灵活性增强,对于新产品的创建,只需要写一个相应的工厂类
- 典型的解耦框架。高层模块只需要知道产品的抽象类,无需关心其它实现类,满足迪米特法则,依赖倒置原则和里式替换原则
缺点:
- 类的个数容易过多,增加复杂度
- 增加了系统的抽象性和理解难度
- 抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决
实现
工厂方法模式由抽象工厂、具体工厂、抽象产品和具体产品等4个要素构成。 ```java package FactoryMethod;
import java.awt.; import javax.swing.;
public class AnimalFarmTest { public static void main(String[] args) { try { Animal a; AnimalFarm af; af = (AnimalFarm) ReadXML2.getObject(); a = af.newAnimal(); a.show(); } catch (Exception e) { System.out.println(e.getMessage()); } } }
//抽象产品:动物类 interface Animal { public void show(); }
//具体产品:马类 class Horse implements Animal { JScrollPane sp; JFrame jf = new JFrame(“工厂方法模式测试”);
public Horse() {
Container contentPane = jf.getContentPane();
JPanel p1 = new JPanel();
p1.setLayout(new GridLayout(1, 1));
p1.setBorder(BorderFactory.createTitledBorder("动物:马"));
sp = new JScrollPane(p1);
contentPane.add(sp, BorderLayout.CENTER);
JLabel l1 = new JLabel(new ImageIcon("src/A_Horse.jpg"));
p1.add(l1);
jf.pack();
jf.setVisible(false);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //用户点击窗口关闭
}
public void show() {
jf.setVisible(true);
}
}
//具体产品:牛类 class Cattle implements Animal { JScrollPane sp; JFrame jf = new JFrame(“工厂方法模式测试”);
public Cattle() {
Container contentPane = jf.getContentPane();
JPanel p1 = new JPanel();
p1.setLayout(new GridLayout(1, 1));
p1.setBorder(BorderFactory.createTitledBorder("动物:牛"));
sp = new JScrollPane(p1);
contentPane.add(sp, BorderLayout.CENTER);
JLabel l1 = new JLabel(new ImageIcon("src/A_Cattle.jpg"));
p1.add(l1);
jf.pack();
jf.setVisible(false);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //用户点击窗口关闭
}
public void show() {
jf.setVisible(true);
}
}
//抽象工厂:畜牧场 interface AnimalFarm { public Animal newAnimal(); }
//具体工厂:养马场 class HorseFarm implements AnimalFarm { public Animal newAnimal() { System.out.println(“新马出生!”); return new Horse(); } }
//具体工厂:养牛场 class CattleFarm implements AnimalFarm { public Animal newAnimal() { System.out.println(“新牛出生!”); return new Cattle(); } }
工厂方法模式只考虑生产同等级的产品,但是在现实生活汇总许多工厂是综合性的工厂,能生产多等级(种类)的产品,如农场里既养了动物又养了植物等。而抽象工厂模式就能帮我们解决这类问题
<a name="hitvz"></a>
#### 抽象工厂模式
提供一个创建产品簇的接口,其每个子类可以生产一系列相关的产品<br />是一种为访问类提供一个创建一组相关或依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构<br />使用抽象工厂模式一般要满足以下条件:
- 系统中有多个产品族,每个具体工厂创建同一族,但属于不同等级结构的产品
- 系统一次只能消费其中某一族产品,即同族的产品一起使用
<a name="D8M6J"></a>
##### 优缺点
抽象工厂模式除了具有工厂方法模式的优点外,其他主要优点如下:
- 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
- 当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品族
- 抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改源代码,满足开闭原则。
缺点:<br />当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。
<a name="dsbE0"></a>
##### 实现
(1) 抽象工厂:提供了产品的生成方法。
```java
interface AbstractFactory {
public Product1 newProduct1();
public Product2 newProduct2();
}
(2) 具体工厂:实现了产品的生成方法。
class ConcreteFactory1 implements AbstractFactory {
public Product1 newProduct1() {
System.out.println("具体工厂 1 生成-->具体产品 11...");
return new ConcreteProduct11();
}
public Product2 newProduct2() {
System.out.println("具体工厂 1 生成-->具体产品 21...");
return new ConcreteProduct21();
}
}
应用:
package AbstractFactory;
import java.awt.*;
import javax.swing.*;
public class FarmTest {
public static void main(String[] args) {
try {
Farm f;
Animal a;
Plant p;
f = (Farm) ReadXML.getObject();
a = f.newAnimal();
p = f.newPlant();
a.show();
p.show();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
//抽象产品:动物类
interface Animal {
public void show();
}
//具体产品:马类
class Horse implements Animal {
JScrollPane sp;
JFrame jf = new JFrame("抽象工厂模式测试");
public Horse() {
Container contentPane = jf.getContentPane();
JPanel p1 = new JPanel();
p1.setLayout(new GridLayout(1, 1));
p1.setBorder(BorderFactory.createTitledBorder("动物:马"));
sp = new JScrollPane(p1);
contentPane.add(sp, BorderLayout.CENTER);
JLabel l1 = new JLabel(new ImageIcon("src/A_Horse.jpg"));
p1.add(l1);
jf.pack();
jf.setVisible(false);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//用户点击窗口关闭
}
public void show() {
jf.setVisible(true);
}
}
//具体产品:牛类
class Cattle implements Animal {
JScrollPane sp;
JFrame jf = new JFrame("抽象工厂模式测试");
public Cattle() {
Container contentPane = jf.getContentPane();
JPanel p1 = new JPanel();
p1.setLayout(new GridLayout(1, 1));
p1.setBorder(BorderFactory.createTitledBorder("动物:牛"));
sp = new JScrollPane(p1);
contentPane.add(sp, BorderLayout.CENTER);
JLabel l1 = new JLabel(new ImageIcon("src/A_Cattle.jpg"));
p1.add(l1);
jf.pack();
jf.setVisible(false);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//用户点击窗口关闭
}
public void show() {
jf.setVisible(true);
}
}
//抽象产品:植物类
interface Plant {
public void show();
}
//具体产品:水果类
class Fruitage implements Plant {
JScrollPane sp;
JFrame jf = new JFrame("抽象工厂模式测试");
public Fruitage() {
Container contentPane = jf.getContentPane();
JPanel p1 = new JPanel();
p1.setLayout(new GridLayout(1, 1));
p1.setBorder(BorderFactory.createTitledBorder("植物:水果"));
sp = new JScrollPane(p1);
contentPane.add(sp, BorderLayout.CENTER);
JLabel l1 = new JLabel(new ImageIcon("src/P_Fruitage.jpg"));
p1.add(l1);
jf.pack();
jf.setVisible(false);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//用户点击窗口关闭
}
public void show() {
jf.setVisible(true);
}
}
//具体产品:蔬菜类
class Vegetables implements Plant {
JScrollPane sp;
JFrame jf = new JFrame("抽象工厂模式测试");
public Vegetables() {
Container contentPane = jf.getContentPane();
JPanel p1 = new JPanel();
p1.setLayout(new GridLayout(1, 1));
p1.setBorder(BorderFactory.createTitledBorder("植物:蔬菜"));
sp = new JScrollPane(p1);
contentPane.add(sp, BorderLayout.CENTER);
JLabel l1 = new JLabel(new ImageIcon("src/P_Vegetables.jpg"));
p1.add(l1);
jf.pack();
jf.setVisible(false);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//用户点击窗口关闭
}
public void show() {
jf.setVisible(true);
}
}
//抽象工厂:农场类
interface Farm {
public Animal newAnimal();
public Plant newPlant();
}
//具体工厂:韶关农场类
class SGfarm implements Farm {
public Animal newAnimal() {
System.out.println("新牛出生!");
return new Cattle();
}
public Plant newPlant() {
System.out.println("蔬菜长成!");
return new Vegetables();
}
}
//具体工厂:上饶农场类
class SRfarm implements Farm {
public Animal newAnimal() {
System.out.println("新马出生!");
return new Horse();
}
public Plant newPlant() {
System.out.println("水果长成!");
return new Fruitage();
}
}
建造者模式
讲一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象
代理模式
为某个对象提供一种代理以控制该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性
在有些情况下,一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象。例如,购买火车票不一定要去火车站买,可以通过12306网站或者去火车代售点买。又比如软件设计中,要访问的远程对象比较大(如视频或者大图像等),其下载要花很多时间;还有因为安全原因需要屏蔽客户端直接访问真实对象,如某单位的内部数据库等。
应用场景
当无法或不想直接引用某个对象或访问某个对象存在困难时,可以通过代理对象来间接访问。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象
场景:
- 远程代理。这种方式通常是为了隐蔽目标对象存在于不同地址空间的事实,方便客户端访问。例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时,实际访问的是网盘空间。
- 虚拟代理。这种方式通常用于要创建的目标对象开销很大时。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换成真实的对象,消除用户对服务器慢的感觉
- 安全代理。这种方式通常用于控制不同种类客户对真实对象的访问权限
- 智能指引。主要用于调用目标对象时,代理附加一些额外的处理功能。例如,增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它
- 延迟加载。指为了提高系统的性能,延迟对目标的加载。例如,hibernate中就存在属性的延迟加载和关联表的延时加载
-
优缺点
优点
代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用
- 代理对象可以扩展目标对象的功能
代理对象能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性
缺点
可使用动态代理的方式解决以下问题
代理模式会造成系统设计中类的数量增加
- 在哭护短和目标对象之间增加一个代理对象,会造成请求处理速度变慢
-
实现
代理模式的结构比较简单,主要是通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问。
根据代理的创建时期,代理模式分为静态代理和动态代理: 静态:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的.class文件就已经存在了
- 动态:在程序运行时,运用反射机制动态创建而成 ```java package proxy;
public class ProxyTest { public static void main(String[] args) { Proxy proxy = new Proxy(); proxy.Request(); } }
//抽象主题 interface Subject { void Request(); }
//真实主题 class RealSubject implements Subject { public void Request() { System.out.println(“访问真实主题方法…”); } }
//代理 class Proxy implements Subject { private RealSubject realSubject;
public void Request() {
if (realSubject == null) {
realSubject = new RealSubject();
}
preRequest();
realSubject.Request();
postRequest();
}
public void preRequest() {
System.out.println("访问真实主题之前的预处理。");
}
public void postRequest() {
System.out.println("访问真实主题之后的后续处理。");
}
}
<a name="PVkw8"></a>
### 适配器模式
将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能够一起工作<br />在现实生活中,经常出现两个对象因接口不兼容而不能在一起工作的实例,这时需要第三者进行适配。例如,讲中文的人同讲英文的人对话是需要一个翻译,用直流电的笔记本电脑接交流电源时需要一个电源适配器等等。在软件设计中也可能出现:需要开发的具有某种业务功能的组件在现有的组件库中已经存在,但它们与当前系统的接口规范不兼容,如果重新开发这些组件成本又很高,这时使用适配器模式能很好地解决这些问题。<br />适配器模式分为类结构型模式和对象结构型模式,前者的类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少
<a name="Yp1Ij"></a>
#### 优缺点
<a name="CFrlZ"></a>
##### 优点
- 客户端通过适配器可以透明地调用目标接口
- 复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类
- 将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题
- 在很对业务场景中符合开闭原则
<a name="vpdSF"></a>
##### 缺点
- 适配器编写过程需要结合业务场景全面考虑,可能会增加系统的复杂性
- 增加代码阅读难度,降低了代码的可读性,过多使用适配器会使系统代码变得凌乱
<a name="gixXS"></a>
#### 实现
类适配器模式可采用多重继承的方式来实现,但java不支持多继承,不过却可以定义一个适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件<br />对象适配器模式可采用将线头组件库中已经实现的组件引入适配器类中,该类同时实现当前系统的业务接口。
<a name="r4AlK"></a>
##### 类适配器模式的代码实现
```java
package adapter;
//目标接口
interface Target
{
public void request();
}
//适配者接口
class Adaptee
{
public void specificRequest()
{
System.out.println("适配者中的业务代码被调用!");
}
}
//类适配器类
class ClassAdapter extends Adaptee implements Target
{
public void request()
{
specificRequest();
}
}
//客户端代码
public class ClassAdapterTest
{
public static void main(String[] args)
{
System.out.println("类适配器模式测试:");
Target target = new ClassAdapter();
target.request();
}
}
对象适配器的代码实现
package adapter;
//目标接口
interface Target
{
public void request();
}
//适配者接口
class Adaptee
{
public void specificRequest()
{
System.out.println("适配者中的业务代码被调用!");
}
}
//类适配器类
class ClassAdapter extends Adaptee implements Target
{
public void request()
{
specificRequest();
}
}
//客户端代码
public class ClassAdapterTest
{
public static void main(String[] args)
{
System.out.println("类适配器模式测试:");
Target target = new ClassAdapter();
target.request();
}
}
双向适配器模式的代码实现
package adapter;
//目标接口
interface TwoWayTarget
{
public void request();
}
//适配者接口
interface TwoWayAdaptee
{
public void specificRequest();
}
//目标实现
class TargetRealize implements TwoWayTarget
{
public void request()
{
System.out.println("目标代码被调用!");
}
}
//适配者实现
class AdapteeRealize implements TwoWayAdaptee
{
public void specificRequest()
{
System.out.println("适配者代码被调用!");
}
}
//双向适配器
class TwoWayAdapter implements TwoWayTarget,TwoWayAdaptee
{
private TwoWayTarget target;
private TwoWayAdaptee adaptee;
public TwoWayAdapter(TwoWayTarget target)
{
this.target=target;
}
public TwoWayAdapter(TwoWayAdaptee adaptee)
{
this.adaptee=adaptee;
}
public void request()
{
adaptee.specificRequest();
}
public void specificRequest()
{
target.request();
}
}
//客户端代码
public class TwoWayAdapterTest
{
public static void main(String[] args)
{
System.out.println("目标通过双向适配器访问适配者:");
TwoWayAdaptee adaptee=new AdapteeRealize();
TwoWayTarget target=new TwoWayAdapter(adaptee);
target.request();
System.out.println("-------------------");
System.out.println("适配者通过双向适配器访问目标:");
target=new TargetRealize();
adaptee=new TwoWayAdapter(target);
adaptee.specificRequest();
}
}
桥接模式
将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承法关系来实现,从而降低了抽象和实现这两个可变纬度的耦合度遵循了里氏替换原则和依赖倒置原则,最终实现了开闭原则
在现实生活中,某些类具有两个或多个维度的变化,如图形即可按形状分,又可按颜色分。
应用场景
- 当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时
- 当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时
当一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性时
优缺点
优点
抽象与实现分离,扩展能力强
- 符合开闭原则
- 符合合成复用原则
- 其实现细节对客户透明
缺点
由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,能正确地识别出系统中两个或多个独立变化的维度,这增加了系统的理解与设计难度实现
可以将抽象化部分与实现话部分分开,取消二者的继承关系,改用组合关系 ```java package bridge;
public class BridgeTest { public static void main(String[] args) { Implementor imple = new ConcreteImplementorA(); Abstraction abs = new RefinedAbstraction(imple); abs.Operation(); } }
//实现化角色 interface Implementor { public void OperationImpl(); }
//具体实现化角色 class ConcreteImplementorA implements Implementor { public void OperationImpl() { System.out.println(“具体实现化(Concrete Implementor)角色被访问”); } }
//抽象化角色 abstract class Abstraction { protected Implementor imple;
protected Abstraction(Implementor imple) {
this.imple = imple;
}
public abstract void Operation();
}
//扩展抽象化角色 class RefinedAbstraction extends Abstraction { protected RefinedAbstraction(Implementor imple) { super(imple); }
public void Operation() {
System.out.println("扩展抽象化(Refined Abstraction)角色被访问");
imple.OperationImpl();
}
}
<a name="TEsUH"></a>
### 装饰模式
在不改变现有对象结构的情况系,动态的给对象增加一些职责,即增加其额外的功能<br />在现实生活中,常常需要对现有产品增加新的功能或美化其外观,如房子装修、相片加相框等。在软件开发过程中,有时想用一些现存的组件,这些组件可能只是完成了一些核心功能。但在不改变其结构的情况下,可以动态地扩展其功能。
<a name="TwoXW"></a>
#### 应用场景
- 当需要给一个现有类添加附加职责,而又不能采用生成之类的方式进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类
- 当需要通过 对现有的一组基本功能进行拍立组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰器模式却很好实现
- 当对象的功能要求可以动态地添加,也可以再动态地撤销时
- JAVA I/O中也运用了装饰器模式。例如,InputStream的子类FilterInputStream,OutputStream的子类FilterOutputStream等等
```java
BufferedReader in = new BufferedReader(new FileReader("filename.txt"));
String s = in.readLine();
优缺点
优点
- 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
- 通过使用不同装饰类及这些装饰类的排列组合,可以实现不同效果
- 装饰器模式完全遵守开闭原则
缺点
装饰器模式会增加许多子类,过渡使用会增加程序的复杂性实现
通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。如果使用组合关系创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能,这就是装饰器模式的目标 ```java package decorator;
public class DecoratorPattern { public static void main(String[] args) { Component p = new ConcreteComponent(); p.operation(); System.out.println(“————————————————-“); Component d = new ConcreteDecorator(p); d.operation(); } }
//抽象构件角色 interface Component { public void operation(); }
//具体构件角色 class ConcreteComponent implements Component { public ConcreteComponent() { System.out.println(“创建具体构件角色”); }
public void operation() {
System.out.println("调用具体构件角色的方法operation()");
}
}
//抽象装饰角色 class Decorator implements Component { private Component component;
public Decorator(Component component) {
this.component = component;
}
public void operation() {
component.operation();
}
}
//具体装饰角色 class ConcreteDecorator extends Decorator { public ConcreteDecorator(Component component) { super(component); }
public void operation() {
super.operation();
addedFunction();
}
public void addedFunction() {
System.out.println("为具体构件角色增加额外的功能addedFunction()");
}
}
<a name="I9iHA"></a>
### 外观模式
为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问
<a name="qdDbJ"></a>
### 享元模式
运用共享技术来有效地支持大量细粒度对象的复用
<a name="MNCSq"></a>
### 组合模式
将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性
<a name="qHxX3"></a>
### 模板方法模式
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重新定义该算法的某些特定步骤<br />设计一个系统时知道了算法所需要的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体环境相关。例如,去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评级等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的额,可以在父类中实现,但是办理具体业务却因人而异,他可能是存款、取款或者转账等,可以延迟到子类中实现。
<a name="ozMTY"></a>
#### 应用场景
算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现<br />当多个子类存在公共的行为时,可以将其提取 出来并集中到一个公共父类中避免代码重复。首先,要识别现有代码中不不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码<br />当 需要控制子类的扩展时,模板方法只在特定调用钩子操作,这样就允许在这些点进行扩展<br />mybatis,jdbc中均有用到模板方法
<a name="OByoQ"></a>
#### 优缺点
<a name="q3cQH"></a>
##### 优点
- 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展
- 它在父类中提取了公共的部分代码,便于代码复用
- 部分党发是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则
<a name="sOg2e"></a>
##### 缺点
对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,间接地增加了系统实现的复杂度<br />父类中的抽象方法子类实现,子类执行的结果会影响父类的结果,这导致一种反响的控制结构,它提高了代码阅读的难度<br />由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍
<a name="MgzyO"></a>
#### 实现
模板方法模式需要注意抽象类与具体子类之间的协作。它用到可虚拟函数的多态性技术以及“不用调用我,让我来调用你”的反向控制技术。
```java
public class TemplateMethodPattern {
public static void main(String[] args) {
AbstractClass tm = new ConcreteClass();
tm.TemplateMethod();
}
}
//抽象类
abstract class AbstractClass {
//模板方法
public void TemplateMethod() {
SpecificMethod();
abstractMethod1();
abstractMethod2();
}
//具体方法
public void SpecificMethod() {
System.out.println("抽象类中的具体方法被调用...");
}
//抽象方法1
public abstract void abstractMethod1();
//抽象方法2
public abstract void abstractMethod2();
}
//具体子类
class ConcreteClass extends AbstractClass {
public void abstractMethod1() {
System.out.println("抽象方法1的实现被调用...");
}
public void abstractMethod2() {
System.out.println("抽象方法2的实现被调用...");
}
}
策略模式
定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户
命令模式
将一个请求封装为一个对象,是发出请求的责任和执行请求的责任分割开
职责链模式
把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方法去除对象之间的耦合
状态模式
观察者模式
多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为
许多对象并不是独立存在的,其中一个对象的行为发生改变可能会导致一个或者多个其他对象的行为也发生改变。诸如mvc模式中的模型与视图的关系;时间模型中事件源与事件处理者
应用场景
- 对象之间存在一对多关系,一个对象的状态发生改变会影响其他对象
- 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以是它们可以各自独立地改变和复用
- 实现类似广播机制的功能,不需要知道具体的收听者,只需分发广播,系统中感兴趣的对象会自动接收该广播
-
优缺点
优点
降低了目标以观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则
-
缺点
目标与观察者之间的依赖关系并没有完全解耦,而且有可能出现循环引用
- 当观察者对象很多时,通知的发布会话费很多时间,影响程序的效率
实现
实现观察者模式时要注意具体目标对象和具体观察者对象之间不能直接调用,否则将使两者之间紧密耦合起来,这违反了面向对象的设计原则 ```java package net.biancheng.c.observer;
import java.util.*;
public class ObserverPattern { public static void main(String[] args) { Subject subject = new ConcreteSubject(); Observer obs1 = new ConcreteObserver1(); Observer obs2 = new ConcreteObserver2(); subject.add(obs1); subject.add(obs2); subject.notifyObserver(); } }
//抽象目标
abstract class Subject {
protected List
//增加观察者方法
public void add(Observer observer) {
observers.add(observer);
}
//删除观察者方法
public void remove(Observer observer) {
observers.remove(observer);
}
public abstract void notifyObserver(); //通知观察者方法
}
//具体目标 class ConcreteSubject extends Subject { public void notifyObserver() { System.out.println(“具体目标发生改变…”); System.out.println(“———————“);
for (Object obs : observers) {
((Observer) obs).response();
}
}
}
//抽象观察者 interface Observer { void response(); //反应 }
//具体观察者1 class ConcreteObserver1 implements Observer { public void response() { System.out.println(“具体观察者1作出反应!”); } }
//具体观察者2 class ConcreteObserver2 implements Observer { public void response() { System.out.println(“具体观察者2作出反应!”); } }
<a name="DtlnQ"></a>
### 中介者模式
定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解
<a name="OFAvm"></a>
### 迭代器模式
提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示
<a name="HgvdQ"></a>
### 访问者模式
在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问
<a name="qTeDf"></a>
### 备忘录模式
在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它
<a name="mWNno"></a>
#### 优缺点
<a name="Vcs8q"></a>
##### 优点
提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态<br />实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能访问这些状态信息<br />简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。
<a name="IMwG2"></a>
##### 缺点
资源消耗大。如果要保存的内部状态信息过大或者特别频繁,将会占有比较大的内存资源
<a name="Tiif7"></a>
#### 实现
备忘录模式的核心是设计备忘录类以及用于管理备忘录的管理者类
```java
package net.biancheng.c.memento;
public class MementoPattern {
public static void main(String[] args) {
Originator or = new Originator();
Caretaker cr = new Caretaker();
or.setState("S0");
System.out.println("初始状态:" + or.getState());
cr.setMemento(or.createMemento()); //保存状态
or.setState("S1");
System.out.println("新的状态:" + or.getState());
or.restoreMemento(cr.getMemento()); //恢复状态
System.out.println("恢复状态:" + or.getState());
}
}
//备忘录
class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
//发起人
class Originator {
private String state;
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
public Memento createMemento() {
return new Memento(state);
}
public void restoreMemento(Memento m) {
this.setState(m.getState());
}
}
//管理者
class Caretaker {
private Memento memento;
public void setMemento(Memento m) {
memento = m;
}
public Memento getMemento() {
return memento;
}
}
解释器模式
Spring中常用的设计模式
简单工厂
Spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的表示来获得bean对象,但是否是在传入参数后创建还是传入参数前创建这个对象,要根据具体情况来定。如下配置,就是在Hellotxxz类中创建一个itxxzBean:
<beans>
<bean id="singletonBean" class="com.itxxz.HelloItxxz">
<constructor-arg>
<value>Hello! 这是singletonBean!value>
</constructor-arg>
</ bean>
<bean id="itxxzBean" class="com.itxxz.HelloItxxz"
singleton="false">
<constructor-arg>
<value>Hello! 这是itxxzBean! value>
</constructor-arg>
</bean>
</beans>
工厂方法
一般情况下,应用程序有自己的工厂来创建bean。如果将应用程序自己的工厂对象交给spring管理,那么spring管理的就不是普通的bean,而是工厂bean
import java.util.Random;
public class StaticFactoryBean {
public static Integer createRandom() {
return new Integer(new Random().nextInt());
}
}
建一个config.xm配置文件,将其纳入Spring容器来管理,需要通过factory-method指定静态方法名称
<bean id="random"
class="example.chapter3.StaticFactoryBean" factory-method="createRandom" //createRandom方法必须是static的,才能找到 scope="prototype"
/>
测试:
public static void main(String[] args) {
//调用getBean()时,返回随机数.如果没有指定factory-method,会返回StaticFactoryBean的实例,即返回工厂Bean的实例 XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("config.xml")); System.out.println("我是IT学习者创建的实例:"+factory.getBean("random").toString());
}
单例模式
spring下默认的bean均为单例,可以通过singleton=”true/false”或者scope=”?”来指定
适配器
在spring的AOP中,使用的advice来增强被代理类的功能。而spring实现这一AOP功能的原理就是使用代理模式对类进行方法级别的切面增强,即生成的被代理类的代理类,并在代理类的方法前设置拦截器,通过执行拦截器中的内容,增强了代理方法的功能,实现的面向切面编程
//Adapter类接口:Target
public interface AdvisorAdapter {
boolean supportsAdvice(Advice advice);
MethodInterceptor getInterceptor(Advisor advisor);
}
//MethodBeforeAdviceAdapter类,Adapter
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
装饰器
代理
观察者
策略
模板方法
JDBCTemplate中的execute方法