参考:Java设计模式(疯狂Java联盟版).chm
设计模式:共有23种,每个设计模式都是用来解决某个问题的固定步骤【固定解决方案】
一、单例模式
单例模式:根据一个类,不管实例化出多少对象,都为同一个对象
解决一个类在内存中只存在一个对象,想要保证对象的唯一
步骤:
- 为了避免其他程序过多的建立该类对象,禁止其他程序建立该类对象【
构造器私有化
】 - 为了其他程序可以访问该类对象,在本类中自定义一个对象【
类中创建一个私有的本类对象
】 - 方便其他程序对自定义类的对象的访问,对外提供一些访问方式【
定义静态方法获取该对象
】
单例模式分为:
饿汉模式:顾名思义,他是一个懒汉,他不愿意动弹。什么时候需要吃饭了,他就什么时候开始想办法搞点食物。
即懒汉式一开始不会实例化,什么时候用就什么时候new,才进行实例化。
懒汉模式:
顾名思义,他是一个饿汉,他很勤快就怕自己饿着。他总是先把食物准备好,什么时候需要吃了,他随时拿来吃,不需要临时去搞食物。
即饿汉式在一开始类加载的时候就已经实例化,并且创建单例对象,以后只管用即可。
代码案例
package cn.com.example8;
public class SingleClass {
}
// 饿汉模式
class SingleClass0{
// 实例化私有化本类对象
private static SingleClass0 SingleClass0 = new SingleClass0();
// 私有化构造器
private SingleClass0(){}
// 定义静态方法用于获取该对象
public static SingleClass0 getSingleClass0(){
return SingleClass0;
}
}
// 懒汉模式
class SingleClass1{
// 声明一个该类类型的变量
private static SingleClass1 singleClass1 = null;
// 私有化构造器
private SingleClass1(){}
// 定义静态方法用于获取该对象
public static SingleClass1 getSingleClass1(){
// 判断singleClass1的值 null:实例化对象 !null:返回
if(singleClass1 == null){
singleClass1 = new SingleClass1();
}
return singleClass1;
}
}
二、工厂模式
工厂模式:用工厂方法代替new操作的一种模式,也就是说工厂模式就相当于创建实例对象的new【不需要new对象,而是获取对象】,这样会给系统带来更大的可扩展性和降低耦合度
1、简单工厂模式
简单工厂模式不属于23种设计模式,相当于一种编程习惯
简单工厂模式包含如下三种角色:
- 抽象产品:定义了产品的规范,描述了产品的主要特性和功能
- 具体产品:实现或继承抽象产品的子类
- 具体工厂:提供了创建产品的方法,使用者通过该方法来获取产品
代码案例
package cn.com.example8;
// 简单工厂模式
// 具体工厂
public class EasyFactory {
// 获取Coffee对象 -- 方法一
public RuiXingCoffee getRuiXingCoffee(){
return new RuiXingCoffee();
}
public XingBaKeCoffee getXingBaKeCoffee(){
return new XingBaKeCoffee();
}
// 获取Coffee对象 -- 方法二
public Coffee getCoffee(String type){
Coffee coffee = null;
if("ruixing".equals(type)){
coffee = new RuiXingCoffee();
}else if("xingbake".equals(type)){
coffee = new XingBaKeCoffee();
}
return coffee;
}
}
// 抽象产品-抽象类
abstract class Coffee{
}
// 具体产品-子类
class RuiXingCoffee extends Coffee{
}
class XingBaKeCoffee extends Coffee{
}
缺陷:
1、工厂处理创建对象的细节,一旦有了工厂,后期如果需要对象直接从工厂中获取即可,这样也就解除了和实现类的耦合【使用方-实现类
】,但同时产生了新的耦合【工厂-实现类
】。
2、后期如果在添加新的类,就必须更改工厂类的代码,违反了开闭原则。
2、静态工厂模式
静态工厂模式:在开发中也有一部分人将工厂类中的创建对象的功能定义为静态的,这个就是静态工厂模式,它也不是23种设计模式中的。
代码案例
package cn.com.example8;
// 简单工厂模式
// 具体工厂
public class EasyFactory {
// 获取Coffee对象 -- 方法一
public static RuiXingCoffee getRuiXingCoffee(){
return new RuiXingCoffee();
}
public static XingBaKeCoffee getXingBaKeCoffee(){
return new XingBaKeCoffee();
}
// 获取Coffee对象 -- 方法二
public static Coffee getCoffee(String type){
Coffee coffee = null;
if("ruixing".equals(type)){
coffee = new RuiXingCoffee();
}else if("xingbake".equals(type)){
coffee = new XingBaKeCoffee();
}
return coffee;
}
}
// 抽象产品-抽象类
abstract class Coffee{
}
// 具体产品-子类
class RuiXingCoffee extends Coffee{
}
class XingBaKeCoffee extends Coffee{
}
3、工厂方法模式
工厂方法模式:是一种常用的类创建型设计模式,此模式的核心精神是封装类中变化的部分,提取其中个性化善变的部分为独立类,通过依赖注入达到解耦、复用和方便后期维护拓展的目的,它的核心结构有四个角色,分别是
抽象工厂、具体工厂、抽象产品和具体产品
四种角色:
- 抽象产品:定义了产品的规范,描述了产品的主要特性和功能
- 具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它和具体工厂之间—对应
- 具体工厂:主要是实现抽象工厂中的抽象方法,完成具体产品的创建
- 抽象工厂:提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品
代码案例
/**
* 抽象工厂
**/
public interface CoffeeFactory {
Coffee createCoffee();
}
/**
* 具体工厂
*
* 抽象产品为coffee,具体产品为LatteCoffee和AmericanCoffee
* 这种工厂模式可以通过不同的具体工厂创建出不同的具体产品
**/
public class LatteCoffeeFactory implements CoffeeFactory {
public Coffee createCoffee() {
return new LatteCoffee();
}
}
public class AmericanCoffeeFactory implements CoffeeFactory {
public Coffee createCoffee() {
return new AmericanCoffee();
}
}
优点:在获取对象时只需要知道具体工厂的名称就可以得到对应的对象,无须知道具体创建过程,在系统增加新的类时只需要添加对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则
缺点:每增加一个类就要增加一个对应的具体工厂类,增加了系统的复杂度
4、模式扩展【简单工厂+配置文件解除耦合】
可以通过工厂模式+配置文件的方式解除工厂对象和产品对象的耦合,在工厂类中加载配置文件中的全类名,并创建对象进行存储,客户端如果需要对象,直接获取即可
4.1、创建配置文件
ruixing=cn.com.example7.RuiXingCoffee
XingBaKe=cn.com.example7.XingBaKeCoffee
代码案例
public class CoffeeFactory {
private static Map<String,Coffee> map = new HashMap();
// 加载配置文件
static {
Properties p = new Properties();
InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");
try {
p.load(is);
// 遍历Properties集合对象
Set<Object> keys = p.keySet();
for (Object key : keys) {
// 根据键获取值(全类名)
String className = p.getProperty((String) key);
// 获取Class对象
Class clazz = Class.forName(className);
// 实例化对象
Coffee obj = (Coffee) clazz.newInstance();
// 将对象以键值对的形式存入map
map.put((String)key,obj);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取对象时直接根据配置文件中的key获取对应的对象
**/
public static Coffee createCoffee(String name) {
return map.get(name);
}
}
三、模板模式
模板模式:在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。
模板模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤,这里的算法
,我们可以理解为广义上的业务逻辑
,并不特指数据结构和算法中的算法
,这里的算法骨架就是模板
,包含算法骨架的方法就是模板方法
。
检测程序运行时间-代码案例:RunCode.java
package cn.com.example8;
public abstract class RunCode {
// 模板方法
public final void getRunTime(){
// 获取开始时间
long start = System.currentTimeMillis();
// 算法骨架
code();
// 获取结束时间
long end = System.currentTimeMillis();
System.out.println("程序运行时间:"+(end - start));
}
// 算法骨架
abstract void code();
}
TestRunCode.java
package cn.com.example8;
public class TestRunCode extends RunCode{
@Override
void code() {
System.out.println("这是具体算法程序");
}
public static void main(String[] args) {
new TestRunCode().getRunTime();
}
}
四、代理模式
1、代理模式概述
代理模式:为其他对象提供一种代理用于控制对这个对象的访问。
在某些场景下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作用
代理模式三个角色:
- 抽象角色:通过接口或抽象类声明真实角色实现的业务方法
- 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作
- 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用
2、代理模式入门程序
2.1、抽象角色:接口
package cn.com.example8;
// 抽象角色
public interface Target {
void handlerMessage();
}
2.2、真实角色:实现类
package cn.com.example8;
// 真实角色
public class TargetImpl implements Target{
@Override
public void handlerMessage() {
System.out.println("这是真实角色的handlerMessage方法");
}
}
2.3、代理角色:实现类
package cn.com.example8;
// 代理角色
public class TargetProxy implements Target{
// Target属性
public Target target = null;
public TargetProxy(){}
public TargetProxy(Target target){
this.target = target;
}
@Override
public void handlerMessage() {
// 调用其他操作
// 权限校验
target.handlerMessage();
// 调用其他操作
// 日志记录
// 性能监控
}
}
2.4、测试类
package cn.com.example8;
// 测试类
public class TestTargetProxy {
public static void main(String[] args) {
// 实例化真实角色对象
TargetImpl targetImpl = new TargetImpl();
// 实例化代理角色对象 并赋值
TargetProxy targetProxy = new TargetProxy(targetImpl);
// 调用方法
targetProxy.handlerMessage(); // 其实调用的是代理中handlerMessage方法中TargetImpl中的handlerMessage方法
}
}
3、代理模式分类
3.1、静态代理
是由程序员创建或工具生成代理类的源码,在编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。【代理模式入门程序就是静态代理】
3.2、动态代理
是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。
优点
- 职责清晰
- 真实的角色就是实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件事务,附带的结果就是编程简洁清晰
- 中介作用
- 代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用
- 高扩展性
Java动态代理底层实现
在JDK的API中提供了
java.lang.reflect.Proxy
,它可以帮助我们完成动态代理的创建。
注意:在Java中使用Proxy来完成动态代理创建,它只能为目标实现了接口的类创建代理。【动态代理是在内存中直接生成了代理对象】
我们可以使用Proxy的静态方法
newProxyInstance
完成代理对象的创建
代码案例
package cn.com.example8;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Java动态代理
*/
public class TestProxy {
public void testProxy(){
// 实例化目标对象
TargetImpl target = new TargetImpl();
// 通过Proxy类调用静态方法生成目标对象对应的代理对象
Target targetProxy = (Target) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(target, args);
}
});
targetProxy.handlerMessage();
}
}
newProxyInstance参数详解
- ClassLoader
- 目标类的类加载器
- 可以通过反射机制获取
- 目标对象.getClass().getClassLoader()
- Class[] interfaces
- 目标类实现的接口
- 可以通过反射机制获取
- 目标对象.getClass().getInterfaces()
- 以上JVM帮我们创建代理对象
- InvocationHandler
- 用于监听代理对象程序的调用
- 底层:观察者模式
- 如果监听到了代理对象要调用方法时,那么InvocationHandler帮我们调用目标方法
InvocationHandler详解
是一个接口,用于监听代理对象程序的调用
底层:观察者模式
如果监听到了代理对象要调用方法时,那么InvocationHandler帮我们调用目标方法
该接口中有一个invoke方法,当代理对象调用了该方法时,InvocationHandler就会调用invoke方法
invoke方法参数
- Proxy
- 代理对象
- Method
- 目标对象中的目标方法
- Args
- 目标方法的参数
开发中我们常用于性能监控、日志记录、权限校验等功能。