设计模式简介-第一节
分类
总体来说设计模式分为三大类:
-
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
-
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
-
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
一、创建型模式
1、单例模式
1.1、定义:
一个类只能有一个对象实例,并且自行实例化并向整个系统提供这个实例,有以下特点:
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
分为三种
- 饿汉模式
- 懒汉模式
- 双重检查加锁
1.2、饿汉模式
在装载类的时候就创建了对象,这是线程安全的,但是占用空间比较多
public class TestSingleton {
private static TestSingleton testSingleton = new TestSingleton();
private TestSingleton() {
}
public static TestSingleton getInstance() {
return testSingleton;
}
}
1.3、懒汉模式
访问的时候再去创建对象,典型的时间换空间,每次访问都进行判断,会降低访问速度,但是如果访问量比较低就会节约空间。而且非线程安全
public class TestSingleton {
private static TestSingleton testSingleton = null;
private TestSingleton() {
}
public static TestSingleton getInstance() {
if (testSingleton == null) {
return new TestSingleton();
} else {
return testSingleton;
}
}
}
1.4、双重检查加锁
通过双重检查加锁,就可以既实现线程安全,又能够使性能不受很大的影响
并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法后,先检查实例是否存在,如果不存在才进行下面的同步块,这是第一重检查,进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。
“双重检查加锁”机制的实现会使用关键字volatile,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。
public class Singleton {
private volatile static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
//先检查实例是否存在,如果不存在才进入下面的同步块
if(instance == null){
//同步块,线程安全的创建实例
synchronized (Singleton.class) {
//再次检查实例是否存在,如果不存在才真正的创建实例
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
2、工厂模式
工厂模式分为简单工厂和工厂方法和抽象工厂
2.1、简单工厂
一个工厂类,不同条件下实例化不同的对象
public class SimplePizzaFactory {
public Pizza CreatePizza(String ordertype) {
Pizza pizza = null;
if (ordertype.equals("cheese")) { //不同的条件判断
pizza = new CheesePizza();
} else if (ordertype.equals("greek")) {
pizza = new GreekPizza();
} else if (ordertype.equals("pepper")) {
pizza = new PepperPizza();
}
return pizza;
}
}
简单工厂带来的问题就是耦合性太强,修改条件就得修改工厂类,所以就有了工厂方法
2.2、工厂方法
定义一个抽象类,将实例化对象的任务交给抽象类的子类来完成,这样如果需要增加条件那么增加子类就好了,达到了低耦合的目标
//抽象类
public interface OrderPizza {
Pizza CreatePizza(String ordertype) ;
}
//子类
public class LDOrderPizza extends OrderPizza {
Pizza createPizza(String ordertype) {
Pizza pizza = null;
if (ordertype.equals("cheese")) {
pizza = new LDCheesePizza();
} else if (ordertype.equals("pepper")) {
pizza = new LDPepperPizza();
}
return pizza;
}
}
public class NYOrderPizza extends OrderPizza {
Pizza createPizza(String ordertype) {
Pizza pizza = null;
if (ordertype.equals("cheese")) {
pizza = new NYCheesePizza();
} else if (ordertype.equals("pepper")) {
pizza = new NYPepperPizza();
}
return pizza;
}
}
工厂方法也有局限性,就是如果有多个系列产品,那就需要把工厂方法所有的类复制一份,修改成别的产品,为了解决这一问题,抽象工厂出现了
2.3、抽象工厂
披萨的抽象方法改成接口,同样也有例如蛋糕这样的接口,他们分别都有实现的工厂类。然后抽象工厂类里填写创建两个产品的接口方法,抽象工厂的实现子类去真正创建所有的产品
//披萨工厂接口
public interface PizzaFactory {
Pizza CreatePizza(String orderType);
}
//水果披萨工厂
public class FruitPizzaFactory implements PizzaFactory {
public Pizza CreatePizza(String orderType) {
if (orderType.equals("Apple")) {
return new Pizza(30,"Apple");
}
else if (orderType.equals("Banana")) {
return new Pizza(30,"Banana");
} else {
return null;
}
}
}
#抽象工厂
public interface Order {
Pizza CreatePizza(String orderType);
//还可以写别的产品
}
//实现抽象工厂类
public class Supermarket implements Order{
public Pizza CreatePizza(String orderType) {
return new FruitPizzaFactory().CreatePizza(orderType);
}
}
#主方法调用
public class Test {
public static void main(String[] args) {
Order order = new Supermarket();
order.CreatePizza("banana");
}
}
三种方法的比较:
-
简单工厂 : 用来生产同一等级结构中的任意产品。(不支持拓展增加产品)
-
工厂方法 :用来生产同一等级结构中的固定产品。(支持拓展增加产品)
-
抽象工厂 :用来生产不同产品族的全部产品。(支持拓展增加产品;支持增加产品族)
二、结构型模式
1、代理模式
代理模式分为静态代理和动态代理
1.1、静态代理
角色分析:
- 抽象角色:一般用接口或者抽象类
- 真实角色(房东):被代理的角色
- 代理角色(中介):代理真实角色,代理真实角色后,我们一般会有一些附属操作
- 客户(租客):访问代理角色的人
#抽象角色
public interface BuyHouse {
void RentHosue();
}
#房东
public class BuyHouseImpl implements BuyHouse {
@Override
public void RentHosue() {
System.out.println("我要租房");
}
}
#中介
public class RentHouseProxy implements RentHouse {
private RentHouse rentHouse ;
public RentHouseProxy(final RentHouse RentHouse ) {
this.RentHouse = RentHouse ;
}
@Override
public void RentHouse () {
System.out.println("看房");
buyHouse.buyHosue();
System.out.println("收中介费");
}
}
好处是:
- 使得真实角色的操作更加纯粹,不用管一些公共的事物
- 公共事物交给了代理,实现了分工
- 公共业务发生拓展的时候方便管理
缺点是:
- 因为是静态的,所以一个真实角色对应一个代理,耦合性太强
1.2、动态代理
- 动态代理和静态代理的角色是一样的,区别在于代理角色是动态生成的
- 动态代理分为两大类,基于接口的动态代理、基于类的动态代理
基于接口—JDK动态代理
基于类:cglib
java字节码实现:javasist
这里介绍一下基于接口的动态代理,需要了解两个类:Proxy(代理类),InvocationHandler:调用处理程序
Proxy:
InvocationHandler:
例子:
//抽象角色:
package com.wcy.demo;
public interface Rent {
void rent();
}
//房东
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
//动态代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class testProxy implements InvocationHandler {
private Object object;
public void setRent(Rent rent) {
this.object = rent;
}
//生成代理类
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
//classloader用于知道类在哪,object.getClass().getInterfaces()用于知道代理的类,最后一个知道动态代理给谁
}
//处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
System.out.println("执行了" + method.getName() + "方法");
Object result = method.invoke(object,args);
fare();
return result;
}
//代理公共操作
public void seeHouse() {
System.out.println("中介带看房子");
}
public void fare() {
System.out.println("收费");
}
}
//测试代码
public class Client {
public static void main(String[] args) {
//生成真实角色
Host host = new Host();
Host2 host2 = new Host2();
//代理角色
testProxy tproxy = new testProxy();
tproxy.setRent(host);
Rent actual_proxy = (Rent) tproxy.getProxy();
actual_proxy.rent();
//代理第二位房东
tproxy.setRent(host2);
actual_proxy.rent();
}
}
1.3 动态代理源码解析
动态代理关键在于获取代理类,如上所述,getProxy()获取的代理类的方法已经实现了代理,rent方法实际已经变成了invoke里的逻辑,那么是如何实现的呢?
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
//动态代理类非空判断
Objects.requireNonNull(h);
//被代理类接口复制
final Class<?>[] intfs = interfaces.clone();
//安全检查
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* **通过加载器和接口数组获取一个特殊的代理类 (重要)**
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//获取特殊代理类有InvocationHandler参数的构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
//如果构造器权限不是public的,设置未public
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//通过反射,将代理类作为参数,创建被代理对象,此时返回的被代理对象就实现了动态代理
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
可以看到,除了一些必要的安全检查和权限check之外,主要步骤是
- 生产一个能够将 代理类 传入 被代理类 的 特殊代理类
- 通过反射,生产被代理类实例
生产这个特殊代理类的源码有些复杂,底层调用的是ProxyClassFactory.apply方法,核心代码如下
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
//生产对应的被代理类对象
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
2、适配器模式
适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。
主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。
#类适配器
/** 继承USBImpl,有了它的方法,然后本身又是VGA类,既可以把USBImpl适配到VGA上了
*/
public class AdapterUSB2VGA extends USBImpl implements VGA {
@Override
public void projection() {
super.showPPT();
}
}
#对象适配器
/** 继承USBImpl,有了它的方法,然后本身实现VGA类,既可以把USBImpl适配到VGA上了
*/
public class AdapterUSB2VGA extends USBImpl implements VGA {
@Override
public void projection() {
super.showPPT();
}
}
## 对象适配器
/** 通过依赖USBImpl对象,然后本身又是VGA类,既可以把USBImpl适配到VGA上了
*/
public class AdapterUSB2VGA implements VGA {
USB u = new USBImpl();
@Override
public void projection() {
u.showPPT();
}
}
# 接口依赖器(当不想实现适配后的对象所有的方法时使用)
/** 可以先创建一个抽象类,依赖USBImpl对象,然后实现这个抽象类的时候选中对应的方法就好了
*/
public abstract class AdapterUSB2VGA implements VGA {
USB u = new USBImpl();
@Override
public void projection() {
u.showPPT();
}
@Override
public void b() {
};
@Override
public void c() {
};
}
public class AdapterUSB2VGAImpl extends AdapterUSB2VGA {
public void projection() {
super.projection();
}
}
三、行为型模式
1、策略型模式
策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。
其实就是一个函数如果有太多if...else耦合性就太强,那么通过不同的类封装不同的函数,就可以避免,这就是策略模式
//策略接口
public interface Strategy {
public int calc(int num1,int num2);
}
//具体策略类
public class AddStrategy implements Strategy {
@Override
public int calc(int num1, int num2) {
// TODO Auto-generated method stub
return num1 + num2;
}
}
public class SubstractStrategy implements Strategy {
@Override
public int calc(int num1, int num2) {
// TODO Auto-generated method stub
return num1 - num2;
}
}
//环境
public class Environment {
private Strategy strategy;
public Environment(Strategy strategy) {
this.strategy = strategy;
}
public int calculate(int a, int b) {
return strategy.calc(a, b);
}
}
//测试类
public class MainTest {
public static void main(String[] args) {
Environment environment=new Environment(new AddStrategy());
int result=environment.calculate(20, 5);
System.out.println(result);
Environment environment1=new Environment(new SubstractStrategy());
int result1=environment1.calculate(20, 5);
System.out.println(result1);
}
}