Spring IOC 工厂、单例、装饰器
Spring AOP 代理、观察者
Spring MVC 委派
Spring JDBC 模板方法
简单工厂模式:是指由工厂对象决定要创建哪一种产品类的实例。
属于创建型模式,但是不属于设计模式,但是非常常见。
适用场景:
1、工厂类负责创建的对象不多
2、只需要知道创建的参数是什么,不需要知道具体的创建细节。
优点:
1、不需要看到创建的细节
2、只需要知道具体传入创建对象的参数
缺点:
1、不易于扩展,每次有新的创建细节的时候就需要修改工厂类,违背开闭原则。
2、一旦参数输错,就可能得不到想要的结果
示例:例如我要创建一个生产手机的工厂,这个工厂可以生产苹果手机,可以生产三星手机。
public interface Phone {
/**
* 生产手机
*/
void create();
}
public class IphoneImpl implements Phone{
@Override
public void create() {
System.out.println("苹果手机已经生产出来了");
}
}
public class ScanStarImpl implements Phone{
@Override
public void create() {
System.out.println("三星手机已经生产出来了");
}
}
public class PhoneFactory {
public static String Iphone="Iphone";
public static String SanStar="ScanStar";
public Phone createPhone(String className){
if(Iphone.equals(className)){
return new IphoneImpl();
}else if(SanStar.equals(className)){
return new ScanStarImpl();
}else{
return null;
}
}
}
public class test {
public static void main(String[] args) {
PhoneFactory phoneFactory=new PhoneFactory();
Phone phone=phoneFactory.createPhone("Iphone");
Phone phone1=phoneFactory.createPhone("SanStar");
Phone phone2=phoneFactory.createPhone("");
phone.create();
phone1.create();
phone2.create();
}
}
工厂方法模式:将工厂类抽象出一个接口,将对象的创建延迟到子类。
属于创建型模式。
适用场景:
1、创建对象需要大量重复的代码,可以将公共的逻辑提取出来
2、客户端不依赖于产品类如何被创建、实现等细节
3、一个类通过其子类来指定创建哪个对象
优点:
1、用户只需要关心所需产品对应的工厂,无须关心创建细节
2、加入新产品符合开闭原则,提高了系统的可扩展性
缺点:代码复杂度变高、代码更抽象不易理解
public interface AbstractFactory {
/**
* 统一生产手机的工厂接口
* @return
*/
Phone createPhone();
}
public class IphoneFactoryImpl implements AbstractFactory{
@Override
public Phone createPhone() {
return new IphoneImpl();
}
}
public class SanStarFactoryImpl implements AbstractFactory{
@Override
public Phone createPhone() {
return new ScanStarImpl();
}
}
public class test {
public static void main(String[] args) {
AbstractFactory abstractFactory=new IphoneFactoryImpl();
abstractFactory.createPhone().create();
AbstractFactory abstractFactory1=new SanStarFactoryImpl();
abstractFactory1.createPhone().create();
}
}
抽象工厂模式:是指提供一个创建一系列相关或相互依赖的接口,无须指定他们具体的类。
属于创建型模式。
适用场景:一个产品族,例如有3个不同的厂,但是不同的厂都可以生产手机,可以生产电脑。
优点:
1、具体产品在应用层代码隔离,无须关心创建细节
2、将一个系列的产品族统一到一起创建
缺点:
1、规定了可能被创建的产品集合,产品族中扩展的产品困难,需要修改抽象工厂的接口
2、增加了系统的抽象性和理解难度
public interface Computer {
/**
* 生产电脑
*/
void create();
}
public class IphoneComputer implements Computer{
@Override
public void create() {
System.out.println("苹果电脑生产出来了");
}
}
public class SanStarComputer implements Computer{
@Override
public void create() {
System.out.println("三星电脑生产出来了");
}
}
public interface AbstractMultiProductFactory {
/**
* 生产手机
* @return
*/
Phone createPhone();
/**
* 生产电脑
* @return
*/
Computer createComputer();
}
public class IphoneMultiFactory implements AbstractMultiProductFactory{
@Override
public Phone createPhone() {
return new IphoneImpl();
}
@Override
public Computer createComputer() {
return new IphoneComputer();
}
}
public class SanStarMultiFactory implements AbstractMultiProductFactory{
@Override
public Phone createPhone() {
return new ScanStarImpl();
}
@Override
public Computer createComputer() {
return new SanStarComputer();
}
}
public class test {
public static void main(String[] args) {
AbstractMultiProductFactory abstractMultiProductFactory=new IphoneMultiFactory();
AbstractMultiProductFactory abstractMultiProductFactory1=new SanStarMultiFactory();
abstractMultiProductFactory.createPhone().create();
abstractMultiProductFactory1.createPhone().create();
abstractMultiProductFactory.createComputer().create();
abstractMultiProductFactory1.createComputer().create();
}
}
概念:无论何种情况,实例只有一个,构造方法私有化,提供一个全局访问点。属于创建型模式。
优点:
1、在内存中只有一个实例,减少了内存开销。
2、可以避免对资源的多重占用
3、设置全局访问点,严格控制访问
缺点:
1、没有接口,扩展困难
2、如果要扩展单例对象,只有修改代码,没有其他途径
饿汉模式:在首次加载时就创建实例
缺点:浪费内存空间
public class HungrySingleton {
private static final HungrySingleton hungrySingleton = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return hungrySingleton;
}
}
懒汉模式:双重锁机制确保多线程情况也只有一个实例
public class LazySingleton {
private volatile static LazySingleton lazySingleton;
private LazySingleton(){}
public static LazySingleton getInstance(){
if(lazySingleton==null){
synchronized (LazySingleton.class){
if(lazySingleton==null){
lazySingleton=new LazySingleton();
//CPU执行时候会转换成JVM指令执行
//1、分配内存空间给这个对象
//2、初始化对象
//3、将初始化好的对象和内存地址建立关联,赋值
//4、用户初次访问
}
}
}
return lazySingleton;
}
}
第二种懒汉模式:使用静态内部类,在调用全局接口的时候才会初始化内部类的内容。
性能最优的一种写法
1、没有用到synchronized
2、巧妙运用了内部类的特性
public class LazyStrongerSingleton {
private LazyStrongerSingleton(){}
public static final LazyStrongerSingleton getInstance(){
return Lazy.lazy;
}
static class Lazy{
private static final LazyStrongerSingleton lazy=new LazyStrongerSingleton();
}
}
public class LazyStrongerSingleton {
private LazyStrongerSingleton(){
//防止别人用反射的方式获取到构造器,然后创建实例
if(Lazy.lazy!=null){
throw new RuntimeException("不允许创建多个实例");
}
}
public static final LazyStrongerSingleton getInstance(){
return Lazy.lazy;
}
static class Lazy{
private static final LazyStrongerSingleton lazy=new LazyStrongerSingleton();
}
//反序列化也会破坏单例
//重写readResolve方法,只不过是覆盖了反序列化出来的对象
//会使用反射判断是否含有该方法,还是创建了两次,发生在JVM层面,相对来说比较安全
//之前反序列的对象会被GC回收
private Object readResolve(){
return Lazy.lazy;
}
}
注册式模式:将每一个实例缓存在统一的容器中,使用唯一标识获取实例
本质上是饿汉式,但是从JDK层面解决了反射破坏和反序列化破坏问题。
public enum RegisterSingleton {
INSTANCE;
private Object Data;
public Object getData() {
return Data;
}
public void setData(Object data) {
Data = data;
}
public static RegisterSingleton getInstance() {
return INSTANCE;
}
}
容器式单例:属于懒加载,但是方便对象管理
public class ContainerSingleton {
private ContainerSingleton(){}
private static Map ioc=new ConcurrentHashMap<>();
public static Object getBean(String className){
synchronized (ioc){
if(!ioc.containsKey(className)){
Object obj=null;
try{
obj=Class.forName(className).newInstance();
ioc.put(className,obj);
}catch (Exception e){
e.printStackTrace();
}
return obj;
}
}
return ioc.get(className);
}
}
第三种注册式单例,伪线程安全,保证线程内部的全局统一
使用ThreadLocal来实现多数据源动态切换
public class ThreadLocalSingleton {
private ThreadLocalSingleton(){}
private static final ThreadLocal threadLocalInstance=
new ThreadLocal(){
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
public static ThreadLocalSingleton getInstance(){
return threadLocalInstance.get();
}
}
委派模式:负责任务的调度和分配任务,和代理模式很像,可以看做是一种特殊情况下的静态代理的全权代理,但是代理模式注重过程,而委派模式注重结果。
不属于GOF23种设计模式之一。
属于行为型模式。
public class Boss {
public void doing(String command,Leader leader){
leader.doing(command);
}
}
public class Leader {
//需要了解员工的特长,做一个缓存
Map map=new HashMap<>();
public Leader() {
map.put("解密",new EmployeeA());
map.put("架构",new EmployeeB());
}
public void doing(String command){
map.get(command).doing();
}
}
public interface Employee {
void doing();
}
public class EmployeeA implements Employee{
@Override
public void doing() {
System.out.println("俺最擅长解密,俺做");
}
}
public class EmployeeB implements Employee{
@Override
public void doing() {
System.out.println("俺最擅长架构,俺做");
}
}
public class test {
public static void main(String[] args) {
new Boss().doing("解密",new Leader());
}
}
是指定义了算法家族、分别封装起来,让它们之间可以相互替换,此模式让算法的变化不会影响到使用算法的用户。
可以避免多重分支的if…else…和switch语句
适用场景:
1、假如系统中有很多类,而他们的区别仅仅在于他们的行为不同。
2、一个系统需要动态地在几种算法中选择一种
public interface BuyStrategy {
void buy();
}
public class MinusBuy implements BuyStrategy{
@Override
public void buy() {
System.out.println("我是最小买的策略");
}
}
public class PlusBuy implements BuyStrategy{
@Override
public void buy() {
System.out.println("我是加大买的策略");
}
}
public class Environment {
private BuyStrategy strategy;
public Environment(BuyStrategy strategy) {
this.strategy = strategy;
}
public void buy()
{
strategy.buy();
}
}
public class test {
public static void main(String[] args) {
new Environment(new MinusBuy()).buy();
new Environment(new PlusBuy()).buy();
}
}
优点:
1、策略模式符合开闭原则
2、避免使用多重条件转移语句,如if…else语句
3、使用策略模式可以提高算法的保密性和安全性
缺点:
1、客户端必须知道所有的策略,并且自行决定使用哪一个策略类
2、代码中会产生非常多策略类,增加维护难度
如果一个行为有多种策略,策略比较复杂,使用if…else维护困难,那么是适合使用策略模式的。
模板模式通常又叫做模板方法模式是指定义一个算法的骨架,并允许子类为一个或者多个步骤提供实现。
模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。
属于行为型设计模式。
public abstract class OpenBox {
public OpenBox() {
this.openBox();
this.takeSomething();
this.cloneBox();
}
protected void takeSomething(){
String object=getSomething();
System.out.println(object+"被拿走了");
}
//钩子方法
protected abstract String getSomething();
protected void cloneBox(){
System.out.println("关闭盒子");
}
protected void openBox(){
System.out.println("打开盒子");
}
}
public class TakeBanana extends OpenBox{
@Override
protected String getSomething() {
return "香蕉";
}
}
public class test {
public static void main(String[] args) {
new TakeBanana();
}
}
适用场景:
1、一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
2、各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复。
优点:
1、提高代码的复用性
2、提高代码的可扩展性
3、符合开闭原则
缺点:
1、类数目的增加
2、间接地增加了系统实现的复杂度
3、继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要改一遍
观察者定义了对象之间的一对多依赖,让多个观察者同时监听一个主体对象,当主题对象发生变化时,它的所有依赖者都会收到消息并更新。
有时也叫做发布订阅模式。
public class Worm {
private String name;
private int number;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
//被观察者
public class BirdPool extends Observable {
private static BirdPool birdPool;
private BirdPool() {
}
public static BirdPool getBirdPool(){
if(birdPool==null){
birdPool=new BirdPool();
}
return birdPool;
}
public void notifyBird(Worm worm){
System.out.println(worm.getName()+"出现了"+worm.getNumber()+"只");
setChanged();
notifyObservers(worm);
}
}
//观察者
public class Bird implements Observer {
private String name;
public Bird(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
BirdPool birdPool=(BirdPool)o;
Worm worm=(Worm)arg;
System.out.println("===================\n"+name+"你可以吃饭了\n"+birdPool.getClass().getName()+
"发现\n"+worm.getName()+"出现了"+worm.getNumber()+"只");
}
}
//jdk提供的一种观察者模式实现的方式
public class test {
public static void main(String[] args) {
Bird bird=new Bird("麻雀");
Bird bird1=new Bird("凤凰");
Bird bird2=new Bird("野鸡");
Worm worm=new Worm();
worm.setName("毛毛虫");
worm.setNumber(3);
BirdPool birdPool=BirdPool.getBirdPool();
birdPool.addObserver(bird);
birdPool.addObserver(bird1);
birdPool.addObserver(bird2);
birdPool.notifyBird(worm);
}
}
优点:
1、观察者和被观察者之间建立了一个抽象的耦合
2、观察者模式支持广播通信
缺点:
1、观察者之间有过多的细节依赖,提高时间消耗及程序的复杂度
2、使用要得当,要避免循环调用
代理模式:是指为其他对象提供一种代理,以控制对这个对象的访问。
代理对象在客户端和目标对象之间起到中介作用。
属于结构型设计模式。
代理模式的适用场景:保护目标对象、增强目标对象
这里主要讲java代理:
第一种:静态代理
代理类和被代理的类实现同一个接口。
public interface FindLove {
void findLove();
}
public class Man implements FindLove{
@Override
public void findLove() {
System.out.println("找到媳妇了");
}
}
public class MatchMaker implements FindLove{
private Man man;
public MatchMaker(Man man) {
this.man = man;
}
@Override
public void findLove() {
System.out.println("帮忙找人选");
man.findLove();
System.out.println("主持婚礼");
}
}
public class testProxy {
public static void main(String[] args) {
Man man=new Man();
MatchMaker matchMaker=new MatchMaker(man);
matchMaker.findLove();
}
}
第二种:动态代理
基于接口代理(jdk)
public class JdkProxy implements InvocationHandler {
//代理的对象
private FindLove man;
public JdkProxy(FindLove man) {
this.man = man;
}
//实现接口
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object result=method.invoke(man,args);
System.out.println("after");
return result;
}
}
public class testJdkProxy {
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
FindLove man= (FindLove)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class[]{FindLove.class},
new JdkProxy(new ManNumberTwo()));
man.findLove();
}
}
总结:代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能使用动态代理
基于继承代理(cglib)
组成部分:
实现类:实现具体目标的逻辑
代理类:实现MethodInterceptor接口,扩展逻辑实现
Enhancer:设置代理类,并且生成代理对象
优点:实现了不适用接口就可以实现动态代理
缺点:实现类没有统一的限定格式
需要加入相关依赖
cglib
cglib
3.2.11
public class Student {
public void targetStudent(){
System.out.println("找到目标学生");
}
}
public class cglibProxy implements MethodInterceptor {
private Object target;
public cglibProxy(Object target) {
this.target = target;
}
public Object getInstance(){
Enhancer en=new Enhancer();
en.setSuperclass(target.getClass());
en.setCallback(this);
return en.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代理之前");
Object result=method.invoke(target,objects);
System.out.println("代理之后");
return result;
}
}
public class testCglib {
public static void main(String[] args) {
Student target=new Student();
Student student=(Student)new cglibProxy(target).getInstance();
student.targetStudent();
}
}
适配器模式是指将一个类的接口转换成客户期望的另一个接口,使原本的接口不兼容的类可以在一起工作。
属于结构型设计模式。
适用场景:
1、已经存在的类,它的方法和需求不匹配的情况。
2、适配器不是软件设计阶段考虑的设计模式,是随着软件维护由于不同产品、不同厂家造成功能类似而接口不相同的解决方案。
public class Login {
public void login(){
System.out.println("老系统登陆成功了");
}
}
public interface NewLogin {
public void newLogin();
}
public class LoginAdapter implements NewLogin{
private Login login;
public LoginAdapter(Login login) {
this.login = login;
}
@Override
public void newLogin() {
login.login();
System.out.println("新系统也可以登录了");
}
}
我写的例子类似于静态代理,但是主要的目的,是使得新老版本可兼容。
优点:
1、能提高类的透明性和复用,现有的类复用但不需要改变
2、目标类和适配器类解耦,提高程序的可扩展性
3、在很多业务场景中符合开闭原则
缺点:
1、适配器编写过程需要全面考虑,可能会增加系统的复杂性
2、增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。
装饰器模式是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能)。
适用场景:
1、用于扩展一个类的功能或给一个类添加附加职责。
2、动态的给一个对象添加功能,这些功能可以再动态的撤销。
public abstract class cake {
abstract String getMsg();
abstract int getPrice();
}
//被装饰者
public class cakeInstance extends cake{
@Override
String getMsg() {
return "一个蛋糕";
}
@Override
int getPrice() {
return 0;
}
}
public abstract class cakeDecorator extends cake{
private cake c;
public cakeDecorator(cake c) {
this.c = c;
}
@Override
String getMsg() {
return c.getMsg();
}
@Override
int getPrice() {
return c.getPrice();
}
}
public class eggDecorator extends cakeDecorator{
public eggDecorator(cake c) {
super(c);
}
@Override
String getMsg() {
return super.getMsg()+"1个鸡蛋";
}
@Override
int getPrice() {
return super.getPrice()+1;
}
}
public class fruitDecorator extends cakeDecorator{
public fruitDecorator(cake c) {
super(c);
}
@Override
String getMsg() {
return super.getMsg()+"一个水果";
}
@Override
int getPrice() {
return super.getPrice()+2;
}
}
public class test {
public static void main(String[] args) {
cake c = new cakeInstance();
c = new eggDecorator(c);
c = new eggDecorator(c);
c = new fruitDecorator(c);
System.out.println(c.getMsg() + "总价:" + c.getPrice());
}
}
优点:
1、装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象扩展功能,即插即用。
2、通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。
3、装饰者完全遵守开闭原则
缺点:
1、会出现更多的代码,更多的类,增加程序复杂性
2、动态装饰时,多层装饰时会更复杂
工厂类一般就是被设计为单例。
例如:ApplicationContext
工厂模式包含工厂方法和抽象工厂属于创建型模式
策略模式属于行为型模式
工厂模式的主要目的是封装好创建逻辑,策略模式接受工厂创建好的对象,从而实现不同的行为
1、策略模式是委派模式内部的一种实现形式,策略模式关注的是结果是否能够相互替代
2、委派模式更关注分发和调度的过程
工厂模式是模板方法的一种特殊实现
1、模板方法和策略模式都有封装算法
2、策略模式是使不同算法可以相互替换,且不影响客户端应用层的使用
3、模板方法是针对定义一个算法的流程,将一些有细微差异的部分交给子类实现。策略模式算法算法实现是封闭的
4、模板模式不能改变算法流程,策略模式可以改变算法流程,且可替换。策略模式通常用来代替if…else…等条件分支语句
1、装饰者模式关注点在于给对象动态扩展、添加方法,而代理更加注重控制对对象的访问。
2、代理模式通常会在代理类中创建被代理对象的实例,而装饰者模式通常把被装饰者作为构造参数。
1、装饰者模式和适配器模式都属于包装器模式
2、装饰者模式可以实现被装饰者与相同的接口或者继承被装饰者作为它的子类,而适配器和被适配者可以实现不同的接口
适配器可以结合静态代理来实现,保存被适配对象的引用,但不是唯一的实现方式
在适配业务复杂的情况下,利用策略模式优化动态适配逻辑