设计模式一般可以分为三大类
- 创建型:工厂模式,单例模式
- 结构型:装饰器模式,适配器模式
- 行为型:策略模式,观察者模式
设计原则:
- 单一职责原则
- 开放封闭原则
- 里式替换原则
- 接口隔离原则
- 依赖倒置原则
- 迪米特原则
- 合成复用原则
代理模式
代理模式是在不改变原先对象的前提下,通过生成一个代理对象,扩展原先对象的一些功能。比如aop,不入侵业务代码实现方法前后逻辑。
静态代理
静态代理是写死的,也就是说需要程序员手动写一个proxy代理类,与原先类共同继承一个接口(个人理解其实从实现层面是可以不共同实现一个接口的,但是这样就会导致管理混乱,代理对象如果能复用这个接口那就会逻辑上通顺很多)
静态代理实现步骤:
- 定义一个接口及其实现类;
- 创建一个代理类同样实现这个接口
- 将目标对象注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。这样的话,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情。
一旦要新增方法,代理对象也会改变,不满足设计模式中开放封闭的原则。这些代理类实际都是在编译过程中产生的class文件
动态代理
分为jdk动态代理和gclib。前者必须要求代理类实现了某个接口,后者没有这点限制。但是相对于jkd而言,gclib效率较低速度较慢。gclib的原理是生成一个子类来代理父类,由于不能继承final类,所以gclib不能代理final类
jdk动态代理
- Proxy.newProxyInstance方法,得到动态代理对象。传入参数是对象的类加载器,类的接口和一个InvocationHandler自定义的处理逻辑
- 新建一个类实现InvocationHandler,这里面只有一个方法invoke,包装了反射中的method.invoke,我们需要在这句调用前后加上自己的逻辑就可以简单实现aop。
- 新建一个工厂类的静态方法来获取这个动态代理对象
- 使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
public interface HelloService { void sayHello(String name); }
public class HelloServiceImpl implements HelloService { @Override public void sayHello(String name) { System.out.println("你好," + name + "!"); } }
public class HelloInvocationHandler implements InvocationHandler { private Object target;
public HelloInvocationHandler(Object target) { this.target = target; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("方法调用前的处理逻辑"); Object result = method.invoke(target, args); System.out.println("方法调用后的处理逻辑"); return result; } }
public class ProxyDemo { public static void main(String[] args) { HelloService helloService = new HelloServiceImpl();
InvocationHandler handler = new HelloInvocationHandler(helloService);
HelloService proxyInstance = (HelloService) Proxy.newProxyInstance( helloService.getClass().getClassLoader(), helloService.getClass().getInterfaces(), handler );
proxyInstance.sayHello("张三"); } }
|
gclib动态代理
- 首先自定义MethodInterceptor,这一点与上面的InvocationHandler是一致的。但是注意这里不是method.invoke来调用实际逻辑了。这里用methodProxy来调用原始方法。
- 使用的时候首先创建增强类Enhancer
- 设置类加载器和父类(也就是被代理对象)
- 设置拦截器,就是第一点创建的对象
- 最后获取这个代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class Person { public void sayHello(String name) { System.out.println("你好," + name + "!"); } }
public class PersonMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("代理类:方法调用前的处理");
Object result = proxy.invokeSuper(obj, args);
System.out.println("代理类:方法调用后的处理");
return result; } }
public class CglibProxyDemo { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Person.class); enhancer.setCallback(new PersonMethodInterceptor());
Person proxyPerson = (Person) enhancer.create();
proxyPerson.sayHello("张三"); } }
|
在spring aop中,如果某个类实现了接口,则默认使用jdk动态代理;否则使用cglib
单例模式
保证只有一个全局访问点,无论怎么访问都只有一个对象。
饿汉式单例
1 2 3 4 5 6 7
| public class singleton { private static final singleton INSTANCE = new singleton(); private singleton(){} public static singleton getInstance(){ return INSTANCE; } }
|
在类加载初始化的时候就直接创建单例对象,适合于程序运行期间始终都需要这个对象的场景。
懒汉式单例
1 2 3 4 5 6 7 8 9 10
| public class singleton { private static singleton INSTANCE; private singleton(){} public static singleton getINSTANCE(){ if (INSTANCE == null){ INSTANCE = new singleton(); } return INSTANCE; } }
|
但是可能会有并发安全问题,如果两个都没有获取到当前对象就会产生覆盖或者数据不一致,可以使用synchronized来锁住这个方法,但是会影响性能。
双重校验锁
上面那种情况下无论是否已经实例化了都需要获取锁,但其实如果其实已经实例化了就不需要上锁。还有一个关键问题是会指令重排。一个对象在创建1过程中会经历三步:
- 分配内存空间
- 初始化对象
- 指针指向该对象
如果3重排就会变成132,此时还没有进行初始化。两个线程并发就会导致报空指针异常。为了解决这个问题需要volatile禁止指令重排。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class Singleton { private Singleton() {}
private static volatile Singleton instance;
public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
|
内部静态类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Singleton { private Singleton() {}
private static class Holder { private static final Singleton INSTANCE = new Singleton(); }
public static Singleton getInstance() { return Holder.INSTANCE; } }
|
内部静态类在被调用的时候才会被加载,而且类加载不会有线程安全问题,天然解决并发问题。
单例模式是可以被破坏的,只要通过反射拿到private的构造器就可以了
策略模式
一种方法可以用多种算法实现就可以使用策略模式,根据不同情况下用不同的算法。也可以消除”if-else”这样的控制语句。
- 上下文:持有一个策略的引用,并提供一个接口来调用策略
- 策略接口:定义了一系列算法的公共接口
- 具体实现:策略的具体实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
|
public interface ReceiptHandleStrategy {
void handleReceipt(Receipt receipt); }
public class Mt1011ReceiptHandleStrategy implements ReceiptHandleStrategy {
@Override public void handleReceipt(Receipt receipt) { System.out.println("解析报文MT1011: " + receipt.getMessage()); } }
public class Mt2101ReceiptHandleStrategy implements ReceiptHandleStrategy {
@Override public void handleReceipt(Receipt receipt) { System.out.println("解析报文MT2101: " + receipt.getMessage()); } }
public class ReceiptStrategyContext {
private ReceiptHandleStrategy receiptHandleStrategy;
public void setReceiptHandleStrategy(ReceiptHandleStrategy receiptHandleStrategy) { this.receiptHandleStrategy = receiptHandleStrategy; }
public void handleReceipt(Receipt receipt){ if(receipt != null){ receiptHandleStrategy.handleReceipt(receipt); } } }
public class ReceiptHandleStrategyFactory {
public ReceiptHandleStrategyFactory() { }
private static Map<String,ReceiptHandleStrategy> strategyMap;
public static void init(){ strategyMap = new HashMap<>(); strategyMap.put("MT1011",new Mt1011ReceiptHandleStrategy()); strategyMap.put("MT2101",new Mt2101ReceiptHandleStrategy()); }
public static ReceiptHandleStrategy getReceiptHandleStrategy(String receiptType){ return strategyMap.get(receiptType); } }
public class Client {
public static void main(String[] args) {
List<Receipt> receiptList = ReceiptBuilder.genReceiptList();
ReceiptStrategyContext context = new ReceiptStrategyContext();
for (Receipt receipt : receiptList) { ReceiptHandleStrategyFactory.init(); ReceiptHandleStrategy strategy = ReceiptHandleStrategyFactory.getReceiptHandleStrategy(receipt.getType()); context.setReceiptHandleStrategy(strategy); context.handleReceipt(receipt); } } }
|
适配器模式
让一个实现类满足统一的接口,使不同的实现类可以一起工作。满足开放封闭原则。
- 例如在Spring AOP中不同的Advice实现的方式不一样类也不一样,可能实现的时候这个类的某个方法叫run,另一个叫start,为了是这些实现类完成统一,就需要加一层适配器,把名字都叫做advicerun,给客户端呈现的就是这个名字。
- SpringMVC也是,不同的控制器种类有很多,例如有注解的有自己写的,需要用适配器统一起来
装饰器模式
扩展类的功能,比起直接继承,装饰器模式可以实现单一责任原则,也便于功能的组合,避免类的层次结构复杂。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| public interface Notification { void send(String message); }
public class SimpleNotification implements Notification { @Override public void send(String message) { System.out.println("Sending notification: " + message); } }
public abstract class NotificationDecorator implements Notification { protected Notification decoratedNotification;
public NotificationDecorator(Notification notification) { this.decoratedNotification = notification; }
@Override public void send(String message) { decoratedNotification.send(message); } }
public class EmailNotificationDecorator extends NotificationDecorator { public EmailNotificationDecorator(Notification notification) { super(notification); }
@Override public void send(String message) { super.send(message); sendEmail(message); }
private void sendEmail(String message) { System.out.println("Sending email with message: " + message); } }
public class SMSNotificationDecorator extends NotificationDecorator { public SMSNotificationDecorator(Notification notification) { super(notification); }
@Override public void send(String message) { super.send(message); sendSMS(message); }
private void sendSMS(String message) { System.out.println("Sending SMS with message: " + message); } }
public class Main { public static void main(String[] args) { Notification notification = new SimpleNotification();
notification = new EmailNotificationDecorator(notification); notification.send("Hello!");
System.out.println("----");
notification = new SMSNotificationDecorator(notification); notification.send("Hello!"); } }
|
观察者模式
观察者模式通过定义一对多的依赖关系,使得一个对象状态的改变能够自动通知并更新所有相关对象。它在软件开发中具有广泛的应用,尤其适用于事件驱动系统和需要解耦组件的场景。在Spring框架中,观察者模式通过事件发布和监听机制得到了高效的实现,增强了应用程序的灵活性和可维护性。
尽管观察者模式带来了许多优点,但在使用时也需注意避免过多的观察者导致系统复杂性增加,以及潜在的性能问题。合理地设计和管理观察者列表,确保事件通知的有效性和高效性,是成功应用观察者模式的关键。