设计模式的六大原则
设计大师关心的是建立弹性的设计,可以维护,可以应付变化。
一、单例设计模式
作用:
通过单例模式可以保证系统中,应用该模式的这个类只有一个实例。即一个类只有一个对象实例
单例设计模式实现步骤
- 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
- 在该类内部产生一个唯一的实例化对象
- 定义一个静态方法返回这个唯一对象。
单例设计模式的类型
根据实例化对象的时机单例设计模式又分为以下两种:
- 饿汉式
- 懒汉式
1、饿汉式
public class Person {
// 使用饿汉式单例设计模式: 比较着急,不管要不要获取我这个类的对象,先创建了该对象再说
// 1. 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
private Person(){
}
// 2. 在该类内部产生一个唯一的实例化对象
private static final Person P = new Person();
// 3. 定义一个静态方法返回这个唯一对象。
public static Person getInstance(){
return P;
}
// ....
}
2、懒汉式
public class Person {
// 懒汉式单例: 不着急,只要当你调用了getInstance静态方法获取对象的时候,就创建,其他时候不创建
// 1. 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
private Person(){
}
// 2. 在该类内部产生一个唯一的实例化对象
private static Person p ;// 默认值为null
// 3. 定义一个静态方法返回这个唯一对象。
public static synchronized Person getInstance(){
// 创建Person类的唯一对象
// 判断一下,如果p这个成员变量的值为null,就创建,不为null,说明该对象已经创建了,直接返回即可
if (p == null){
p = new Person();
}
return p;
}
// ...
}
二、多例设计模式
作用:
通过多例模式可以保证系统中,应用该模式的类有固定数量的实例。多例类要自我创建并管理自己的实例,还要向外界提供获取本类实例的方法。
实现步骤
1.创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
2.在该类内部产生固定数量的实例化对象 ----> 集合
3.提供一个静态方法来随机获取一个该类的实例化对象
public class Person {
// 使用多例设计模式: 保证程序运行期间该类只有固定数量的对象产生
// 1.创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
private Person(){
}
// 2.在该类内部产生固定数量的实例化对象 ----> 集合 只能产生依次固定数量的对象
// 2.1 定义一个存放该类对象的集合
private static ArrayList<Person> list = new ArrayList<>();
// 2.2 在静态代码块中,创建固定数量的对象,添加到集合中
static {
// 创建固定数量的该类对象
for (int i = 0; i < 3; i++) {
Person p = new Person();
list.add(p);
}
}
// 3.提高一个静态方法来随机获取一个该了的实例化对象
public static Person getInstance(){
// 创建一个Random对象
Random r = new Random();
// 随机产生一个list集合的索引
int index = r.nextInt(list.size());// [0,3) 0,1,2
// 根据索引获取对象
Person p = list.get(index);
// 返回对象
return p;
}
}
三、工厂设计模式(Factory Pattern)
设计模式之工厂模式(factory pattern)
工厂顾名思义就是创建产品,根据产品是具体产品还是具体工厂可分为简单工厂模式和工厂方法模式,根据工厂的抽象程度可分为工厂方法模式和抽象工厂模式。
这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。之前我们创建类对象时, 都是使用new 对象的形式创建, 除new 对象方式以外, 工厂模式也可以创建对象.
耦合度: 类与类之间的关系,如果关系比较强,高耦合, 如果关系比较弱,低耦合,而开发是要尽量低耦合
作用
将前端代码与要创建的对象分开,前端不需要直接创建对象,也就不需要关心创建对象时需要的数据。只需要通过工厂获取对象即可。
- 解决类与类之间的耦合问题
简单工厂模式实现
Phone接口:
public interface Phone {
void make();
}
MiPhone类:
public class MiPhone implements Phone {
public MiPhone() {
this.make();
}
@Override
public void make() {
// TODO Auto-generated method stub
System.out.println("make xiaomi phone!");
}
}
IPhone类:
public class IPhone implements Phone {
public IPhone() {
this.make();
}
@Override
public void make() {
// TODO Auto-generated method stub
System.out.println("make iphone!");
}
}
PhoneFactory类:
public class PhoneFactory {
public Phone makePhone(String phoneType) {
if(phoneType.equalsIgnoreCase("MiPhone")){
return new MiPhone();
}
else if(phoneType.equalsIgnoreCase("iPhone")) {
return new IPhone();
}
return null;
}
}
测试:
public class Demo {
public static void main(String[] arg) {
PhoneFactory factory = new PhoneFactory();
Phone miPhone = factory.makePhone("MiPhone"); // make xiaomi phone!
IPhone iPhone = (IPhone)factory.makePhone("iPhone"); // make iphone!
}
}
四、动态代理
代理模式概述
代理模式的定义:被代理者没有能力或者不愿意去完成某件事情,那么就需要找个人代替自己去完成这件事,这个人就是代理者, 所以代理模式包含了3个角色: 被代理角色 代理角色 抽象角色(协议)
静态代理
静态代理:
代理模式是指不直接调用实际对象,而是通过调用代理,来间接的调用实际的对象。
为什么要采用这种间接的形式来调用对象呢?
场景①:不想直接访问实际的对象;
场景②:对实际的对象的访问存在困难;
场景③:有需求需要将封装对象访问/操作的成员方法进行逻辑增强,而不修改原方法;
在现实生活中,这种情形非常的常见,比如请一个律师代理来打官司。
静态代理 - 实现步骤
① 自定义一个代理类(增强类)实现和被代理类(被增强类)相同的接口
② 在代理类中声明被代理类的对象(一般在构造方法中创建)
③ 在代理类的方法中使用被代理类调用方法
1.3 静态代理 - 优缺
优点:不修改源码增强被代理类的功能,可以控制被代理类的对象
缺点:必须要重写被代理类接口的所有的方法,耦合性高
动态代理介绍
-
概述 : 动态代理就是直接通过反射生成一个代理对象,代理对象所属的类是不需要存在的
-
动态代理的获取:
jdk提供一个Proxy类可以直接给实现接口类的对象直接生成代理对象
动态代理相关api介绍
Java.lang.reflect.Proxy类可以直接生成一个代理对象
- Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)生成一个代理对象
- 参数1:ClassLoader loader 被代理对象的类加载器
- 参数2:Class<?>[] interfaces 被代理对象的要实现的接口
- 参数3:InvocationHandler h (接口)执行处理类
- 返回值: 代理对象
- 前2个参数是为了帮助在jvm内部生成被代理对象的代理对象,第3个参数,用来监听代理对象调用方法,帮助我们调用方法
- InvocationHandler中的Object invoke(Object proxy, Method method, Object[] args)方法:调用代理类的任何方法,此方法都会执行
- 参数1:代理对象(慎用)
- 参数2:当前执行的方法
- 参数3:当前执行的方法运行时传递过来的参数
- 返回值:当前方法执行的返回值
实现
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
/**
*
*
*/
//@SuppressWarnings("all")
public class Test {
public static void main(String[] args) {
/*
对Collection接口进行代理,以前的remove(Object obj)方法是删除集合中第一次出现的元素
(比如集合中有多个“abc”,调用remove(“abc”)后只会删除一个元素)。
代理后,要求在调用remove(Object obj)方法后,能够删除集合中所有匹配的元素。【动态代理】
*/
// 创建ArrayList集合
Collection<String> col = new ArrayList<>();
// 添加元素
col.add("abc");
col.add("abc");
col.add("bac");
col.add("abc");
col.add("abc");
col.add("abc");
System.out.println("删除前:" + col);// 删除前:[abc, abc, bac, abc, abc, abc]
// 动态代理增强remove方法
Collection<String> proxy = (Collection<String>) Proxy.newProxyInstance(col.getClass().getClassLoader(), col.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
invoke方法:
参数1proxy:表示生成的代理对象,一般不用
参数2method:表示代理对象调用的方法
参数3args:表示代理对象调用方法传入的实际参数
返回值:表示代理对象调用方法的返回值
*/
// 代理对象调用方法就会来到这里,所以增强方法的代码就写在这,就可以了
// 被代理对象执行一次代理对象调用的方法,来确定返回值 删除一个
Object res = method.invoke(col, args);// col.remove("abc") col.toArray();
if (method.getName().equals("remove")) {
// 删除剩余的
// 获取col集合对象的迭代器
Iterator<String> it = col.iterator();
// 使用迭代器进行遍历
while (it.hasNext()) {
// 在循环中,判断遍历出来的元素是否是要删除的元素
String e = it.next();
if (e.equals(args[0])) {
// 如果是,就删除
it.remove();
}
}
}
if (method.getName().equals("toArray")) {
System.out.println("增强toArray方法...");
}
return res;
}
});
// 代理对象删除元素
boolean res = proxy.remove("abc");
System.out.println(res);//true
System.out.println("删除后:" + col);// 删除后:[bac]
Object[] arr = proxy.toArray();
System.out.println(arr);
System.out.println(Arrays.toString(arr));
/*
// 集合对象删除元素
boolean res = col.remove("abc");
System.out.println(res);// true
System.out.println("删除后:"+col);// 删除后:[abc, bac, abc, abc, abc]*/
}
}
五、装饰者设计模式
装饰者模式(Decorator Pattern)概述
缓冲流中涉及到java的一种设计模式,叫做装饰模式。
装饰者模式指的是在不改变原类, 不使用继承的基础上,动态地扩展一个对象的功能。
装饰者模式遵循原则:
- 装饰类和被装饰类必须实现相同的接口
- 在装饰类中必须传入被装饰类的引用(通过构造方法的参数传入)
- 在装饰类中对需要扩展的方法进行扩展
- 在装饰类中对不需要扩展的方法调用被装饰类中的同名方法
public interface Star {
public void sing();
public void dance();
}
public class LiuDeHua implements Star {
@Override
public void sing() {
System.out.println("刘德华在唱忘情水...");
}
@Override
public void dance() {
System.out.println("刘德华在跳街舞...");
}
}
需求:
在不改变原类的基础上对LiuDeHua类的sing方法进行扩展
public class LiuDeHuaWrapper implements Star {
// 存放被装饰类的引用
private LiuDeHua liuDeHua;
// 通过构造器传入被装饰类对象
public LiuDeHuaWarpper(LiuDeHua liuDeHua){
this.liuDeHua = liuDeHua;
}
@Override
public void sing() {
// 对需要扩展的方法进行扩展增强
System.out.println("刘德华在鸟巢的舞台上演唱忘情水.");
}
@Override
public void dance() {
// 不需要增强的方法调用被装饰类中的同名方法
liuDeHua.dance();
}
}
装饰者模式 - 优缺
优点:不修改源码增强被代理类的功能,
缺点:
无法控制被代理类的对象
必须要重写被代理类接口的所有的方法,耦合性高