参考自尚硅谷韩顺平老师的java设计模式:java设计模式
UML类图关系:依赖,泛化(继承),实现,关联,聚和,组合
依赖:一个类中用到了另外一个类(成员变量,方法形参,方法返回值,方法中使用到的类型)
泛化(继承):java中类的继承
实现:java中的类实现接口(虚线表示)
关联(依赖关系的特例):单向一对一(一对多,),双向一对一(一对多)
聚和(关联关系的特列):整体和部分可以分开
组合:整体和部分不可以分开
程序中频繁创建和销毁的对象或者创建重量级实例需要花很长时间的,java.lang.Runtime使用饿汉式,HashMap,TreeSet懒汉式
1、私有构造方法,在类加载时实例化,提供公共的获取实例的方法,
2、缺点:有很多方式导致类加载,可能在程序中其他方式(调用该类的其他静态方法)导致类加载,而我们没有使用这个对象,导致内存浪费
3、实现:①常量,②静态代码块
public class type01_ehan { public static void main(String[] args) { System.out.println(Singletonehan01.getInstance());//调用静态方法,导致类加载。 } } /** * 构造器私有化,公共的提供对象方法, * 常量 */ class Singletonehan01{ public static final int i = 1; private static final Singletonehan01 instance = new Singletonehan01(); private Singletonehan01(){} public static Singletonehan01 getInstance(){return instance;}; static{ System.out.println("Singleton类加载完成"); } } /** * 构造器私有化,公共的提供对象方法 * 静态变量 * 在静态代码块中初始化 */ class Singletonehan02{ private static Singletonehan02 instance; private Singletonehan02(){} public static Singletonehan02 getInstance(){return instance;}; static{ System.out.println("Singletonlan类加载完成"); instance = new Singletonehan02(); } }
1、静态变量,在第一次调用静态方法获取实例时,创建实例并且返回
2、实现:①线程不安全②同步效率低③双重检查(第一个if保证获取实例的效率,第二个if上的synchronized保证同步)(线程安全,效率不低)④静态内部类中私有常量(类加载时初始化,只有需要内部类时(外部类提供的方法)才会初始化,不会因为外部类类加载导致加载实例)⑤枚举:个人觉得也属于饿汉式(类加载时创建实例)
3、volatile:防止指令重排,保证数据可见性,有序性,线程共享一个变量是时,当一个线程修改了变量,其他另一个线程能够知道变量已经被修改
public class type02_lhan { public static void main(String[] args) { } } /** * 静态变量,使用时创建对象 * */ class Singletonlhan{ private static Singletonlhan instance = null; private Singletonlhan(){} public static Singletonlhan getInstance(){ if(instance==null) instance = new Singletonlhan();//非线程安全 return instance; }; } class Singletonlhan02{ private static Singletonlhan02 instance = null; private Singletonlhan02(){} public static synchronized Singletonlhan02 getInstance(){//类锁 if(instance==null) instance = new Singletonlhan02();//非线程安全 return instance; }; } //双重检查 class Singletonlhan03{ private static volatile Singletonlhan03 instance = null; private Singletonlhan03(){} public static Singletonlhan03 synchronized getInstance(){//类锁 if(instance==null) { synchronized (Singletonlhan03.class){if(instance==null) instance=new Singletonlhan03();} }instance = new Singletonlhan03();//非线程安全 return instance; }; } //静态代码块 class Singletonlhan04{ private Singletonlhan04(){} private static class SingletonInstance{ private static final SingletonInstance instance = new SingletonInstance(); static{ System.out.println("内部类类加载"); } } public static SingletonInstance getInstance(){//类锁 return SingletonInstance.instance;//造成类加载,类加载时是线程安全的。 }; } //枚举公共常量 enum SingletonEnum{ INSTANCE;//定义当前类型的常量,并且调用无参数构造方法 SingletonEnum(){ System.out.println("无参构造"); } public static void doSome(){//导致类加载 System.out.println("doSome"); } }
pizza店工作流程,便于扩展和维护:pizza种类,制作流程,订购
①新建抽象类Pizza:声明pizza制作流程的方法,以及添加属性:种类名
public abstract class Pizza { protected String name; public void prepare() { System.out.println(name+" prepare;"); } public void bake() { System.out.println(name+" baking;"); } public void cut() { System.out.println(name+" cutting;"); } public void box() { System.out.println(name+" boxing;"); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
②添加pizza种类继承Pizza抽象类并且重写方法,
GreekPizza实现类
public class GreekPizza extends Pizza { @Override public void prepare(){ System.out.println("为制作希腊披萨准备原材料"); } }
CheesePizza实现类
public class CheesePizza extends Pizza { public CheesePizza() { } @Override public void prepare(){ System.out.println("为制作奶酪披萨准备原材料"); } }
③新建订单类:下单方法,确定制作用户所需的pizza,制作pizza,完成订单
3)
public class OrderPizza { private String orderType; private Pizza pizza; public void order(){//下单,获取用户的pizza类,并且制作 while(true){// orderType = getType(); if(orderType.equals("cheese")) {pizza = new CheesePizza();pizza.setName(orderType);} else if(orderType.equals("greek")) {pizza = new GreekPizza();pizza.setName(orderType);} else pizza = null; if(pizza==null) { System.out.println("没有该种类型的pizza"); continue; } pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); } } //获取用户点单pizza种类 private String getType(){ System.out.println("请输入pizza种类:"); return new Scanner(System.in).next(); } }
Pizza类
思路:在订单类中的下单功能确定pizza对象。
缺点:添加Pizza实现类时,在使用到Pizza实例都需要进行修改
增加pizza的种类时,就需要使用Pizza对象的代码,需要修改订单下单方法(获取Pizza的地方可能有很多),我们可以定义一个工厂类管理为了Pizza对象,所有pizza对象的获取由工厂类提供。所有的Pizza实例,以后添加种类,只需要修改这个工厂类即可,而不需要修改使用这个实例的多处地方。
如果将工厂类的方法是静态的也叫静态工厂
PizzaFactory
public class PizzaFactory { public Pizza createPizza(String orderType){ Pizza pizza = null; if(orderType.equals("cheese")) {pizza = new CheesePizza();pizza.setName(orderType);} else if(orderType.equals("greek")) {pizza = new GreekPizza();pizza.setName(orderType);} else if(orderType.equals("pepper")){pizza = new PepperPizza();pizza.setName(orderType);} return pizza; } }
订单类使用工厂类获取的Pizza类操作,重构OrderPizza
public void order2(){//下单,获取用户的pizza类,并且制作 while(true){// orderType = getType(); pizza = new PizzaFactory().createPizza(orderType); if(pizza==null){ System.out.println("没有该种类型的pizza");continue;} pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); System.out.println("订单完成"); } }
需求扩展:不同种类的pizza又有不同的口味,甜,咸。。。。
原理:将创建实例的方法作为一个抽象类,通过子类重写方法获取实例,对象的实例推迟到子类
将工厂类(订单类)设置为一个抽象类,创建某个类的实例由子类负责。
①新建pizza类,和上面一样
②pizza子类,BjCheesePizza,BjGreekPizza,…,方法重写prepare,和传统方法一样
③订单抽象类,在构造方法中获取种类名,调用子类实现的抽象方法获取pizza类,然后执行pizza的制作方法
④订单抽象类继承类:BjOrderPizza(重写创建pizza的方法,然然后返回北京口味的cheese,greek的pizza),LdOrderPizza(重写方法,然后返回伦敦口味的cheese,greek的pizza)
原理:工厂类抽象,创建实例由其子类实现,相当于是简单工厂模式和工厂方法模式的整合
将工厂类作为一个抽象类,创建其子类实现抽象方法获取实例
①pizza类及其子类相同
②抽象类AbsFactory提供createPizza方法,
③子类LbFactory,BjFactory分别提供北京口味,伦敦口味pizza对象
④订单类OrderPizza,提供order方法,需要传入AbsFactory实现类,然后通过用户输入的种类名获取Pizza,然后调用pizza的制作过程方法
根据aLocale.hasExtensions属性,返回不同的实例类型
将创建类实例的代码块抽取出来,通过工厂类的方法中进行统一的管理
就是根据原型实例对象确定需要创建对象的种类,然后通过拷贝这些原型,创建新的对象
克隆羊:创建10只属性一模一样的对象,
实现:创建羊对象,其他的所有对象都需要从第一个对象中取出属性后然后创建新的对象,保证属性一致
缺点:需要频繁的获取对象的属性和创建对象。
思路:需要克隆的类重写父类Object的clone方法,并且实现Cloneable借口,表示这个类具有可以进行复制克隆,克隆的两个类是不同的
源码探讨:spring的xml配置文件创建对象的protoType类型就是,每次创建的对象都是一个全新的对象,属性值是相同的,但是地址不同。
1)浅拷贝:基本数据类型直接赋值,引用数据类型是引用地址,所以克隆对象和原型对象中的引用数据类型是同一个。改变一个都会改变
2)深拷贝:改变原型中的引用
①对克隆再次拷贝,得到一个新的克隆类然后赋值,这样就不是引用相同的类了,而且属性又是相同的。
②序列化:通过对对象的序列化和反序列化实现。
概念:将一个复杂对象的创建对象的过程和创建对象分离开来,允许用户指定复杂对象的类型和内容,就可以创建,用户不需要知道其内部的具体结构。类似于修房子和汽车生产
①原理
注意和工厂模式的区别,都是用来创建对象的,但是工厂模式不用注意创建对象的细节问题,只负责创建对象就可以了,而建造者模式需要将创建对象的细节和对象分离开来。根据用户的选择由指挥者指挥根据具体创建者(继承于抽象建造者,内部有建造过程的方法,和工厂模式的工厂类一样,创建对象的)创建对象。
②源码分析:
概念:接口与接口之间无法正常对接使用,eg:圆孔耳机,不同国家的插座。适配器就作为中间层,原本不兼容的两个接口兼容。
1、被适配器
2、适配接口
3、适配器
4、手机产品
6、总结
1)空实现:当我们只需要使用到一个接口的某个方法,而不是所有的方法时,我们先用抽象类继承接口重写所有的抽象方法(默认实现,空实现)。这样我们只需要再重写抽象类中的我们需要的方法就可以了。Servlet以及SpringMVC中的DispatcherServlet中的,处理器执行链
2)DispatcherServlet
执行流程,通过DispatcherServelt的doDispatch方法,根据处理器映射器HanndlerMapping找到请求的controller(handler,处理器)(返回处理器执行链,包括处理器和拦截器),然后根据controller(handler,处理)通过处理器适配器handlerAdapter找到匹配的适配器,然后根据适配器去执行handler的方法,返回ModelAndView,最后根据视图解析器,确定静态资源文件(view)和数据(model),将model数据解析到request作用域中。最后返回view视图
多级分类,使用聚和关系,将两个类连接在一起,像之前的工厂模式,创建特定的对象,
被修饰这和修饰者均继承一个基类,在装饰者中声明被装饰者对象,根据实际要求可以重写修饰者中的父类方法
Drink:
public abstract class Drink { private String des;//描述 private float price;//价格 public Drink(){} public Drink(String des,float price){ this.des=des; this.price=price; } public String getDes() { return des; } public void setDes(String des) { this.des = des; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } public abstract float cost();//计算价格 }
装饰者
public abstract class Decorator extends Drink { private Drink obj;//饮料种类 public Decorator(){} public Decorator(Drink obj){ this.obj=obj; } public Drink getObj() { return obj; } public void setObj(Drink obj) { this.obj = obj; } @Override //重写价格计算 public float cost(){ return obj.getPrice()+getPrice(); } }
具体修饰者
public class Milk extends Decorator { public Milk(Drink obj){ setPrice(1f);//设置调料的价格 setDes("牛奶"); setObj(obj); } }
具体被修饰者
public class LongBlack extends Drink { public LongBlack(String des, float price) { super("LongBlack", 5.0f); } @Override public float cost() { float price = getPrice(); System.out.println(getDes()+":"+price+"元"); return getPrice(); } }
IO源码:FilterInputStream中InputStream类的对象
学校院系关系
学校—>学院—>专业(多级分类,感觉可以采用桥接模式)
用处:要处理的对象可以生成一个树形结构,无论是叶子还是节点操作时方法都是一样的。
都需要声明Component抽象类或者接口,leaf或者composite都需要继承或者实现,对所有的叶子和节点都有公共的方法
和生活中的代理一样,通过代理对象调用目标对象的方法,在调用之前或者之后可以进行功能增强。有cglib和jdk两种实现方式,JDK目标类必须实现接口,cglib目标类必须可继承
代理类和目标类实现统一接口
代理对象中调用目标对象的方法并且进行功能增强。
每增加一个代理对象,都需要创建一个代理类
手动编写目标对象的代理类。目标类实现一个接口
编写InvocationHandler接口实现类重写invoke方法,通过这个方法调用目标对象的方法并且进行功能增强。
Proxy.newProxyInstance()方法创建代理对象。
代理对象由Proxy自动生成。
public class MapperHandler<T> implements InvocationHandler { private Class<T> target; public MapperHandler(){this(null);} public MapperHandler(Class<T> target){this.target=target;} @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //获取sql语句和sqlsession, //执行sql语句 //处理结果集 //关闭sqlsession System.out.println("执行了:"+method.getName()+"方法"); return "access"; } public T newInstance(){ return (T) Proxy.newProxyInstance(target.getClassLoader(),new Class[]{target},this); } }
需要导入相应Jar包
创建类实现MethodInterceptor接口,实现interceptor方法实现功能增强。新增newProxyInstance方法创建代理对象。
Iterator:迭代器方法的接口
ConcreteIterator:具体的迭代器实现类,根据集合或者数组,分别实现方法
College:统一的聚和接口,根据集合类型,返回不同的迭代器对象
ConcreteAggregate:返回可以遍历当前元素的迭代器对象
package com.zy.designpatterns.iterator; import java.util.*; import java.util.function.Consumer; public class ComputerCollegeIterator<E> implements Iterator<E> { private E [] elements = null;//存储元素的数组 private int position = -1;//位置 private static final int COMMON_SIZE=10; public ComputerCollegeIterator(E [] elements){ this.elements=elements; } public ComputerCollegeIterator(){ this(null); } @Override public boolean hasNext() { if(position+1>elements.length-1||elements==null) return false; return true; } @Override public E next() { return elements[++position]; } @Override public void remove() { } @Override public void forEachRemaining(Consumer action) { } public E[] getElements() { return elements; } public void setElements(E[] elements) { this.elements = elements; } public int getPosition() { return position; } public void setPosition(int position) { this.position = position; } }
package com.zy.designpatterns.iterator; import java.util.*; import java.util.function.Consumer; public class InfoCollegeIterator<E> implements Iterator<E> { private List<E> elements; private int index = -1; public InfoCollegeIterator(){this(null);} public InfoCollegeIterator(List<E> elements){this.elements=elements;} @Override public boolean hasNext() { if(index+1>elements.size()-1||elements==null) return false; return true; } @Override public E next() { return elements.get(++index); } @Override public void remove() { } @Override public void forEachRemaining(Consumer<? super E> action) { } }