1、23种设计模式
-
创建型模式
用于描述“怎样创建对象”,主要特点是“将对象的创建与使用分离”。
单例、原型、工厂方法、抽象工厂、建造者 5 种。 -
结构型模式
用于描述如何将类或对象按某种布局组成更大的结构,
代理、适配器、桥接、装饰、外观、享元、组合 7 种。 -
行为型模式
用于描述类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务,以及怎样分配职责。
模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等 11 种。
2、软件设计原则
1.开闭原则
对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。
想要达到这样的效果,需要使用接口和抽象类。
2.里氏代换原则
任何基类可以出现的地方,子类一定可以出现。通俗理解:子类可以扩展父类的功能,但不能改变父类原有的功能。换句话说,子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。(【例】正方形不是长方形。)
3.依赖倒转原则
高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。降低客户与实现模块间的耦合。(【例】组装电脑。)
4.接口隔离原则
客户端不应该被迫依赖于它不使用的方法;一个类对另一个类的依赖应该建立在最小的接口上。(防火、防水、防盗安全门)
5.迪米特法则
迪米特法则又叫最少知识原则。只和你的直接朋友交谈,不跟“陌生人”说话。
其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。
6.合成复用原则
合成复用原则是指:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
通常类的复用分为继承复用和合成复用两种。继承复用虽然有简单和易实现的优点,但它也存在以下缺点:
- 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用。
- 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
- 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。
3、创建者模式
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。
创建型模式分为:
- 单例模式
- 工厂方法模式
- 抽象工程模式
- 原型模式
- 建造者模式
3.1 单例设计模式
单例设计模式分类两种:
饿汉式:类加载就会导致该单实例对象被创建
懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建
- 饿汉式-静态变量方式
/** - 饿汉式 - 静态变量创建类的对象 */
public class Singleton {
//私有构造方法
private Singleton() {}
//在成员位置创建该类的对象
private static Singleton instance = new Singleton();
//对外提供静态方法获取该对象
public static Singleton getInstance() {
return instance;
}
}
-
懒汉式-双重检查锁
/** * 双重检查方式 */ public class Singleton { //私有构造方法 private Singleton() {} private static volatile Singleton instance; //对外提供静态方法获取该对象 public static Singleton getInstance() { //第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实际 if(instance == null) { synchronized (Singleton.class) { //抢到锁之后再次判断是否为空 if(instance == null) { instance = new Singleton(); } } } return instance; } } //在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。 //要解决双重检查锁模式带来空指针异常的问题,只需要使用 `volatile` 关键字, `volatile` 关键字可以保证可见性和有序性。
https://img-blog.csdnimg.cn/img_convert/6ad67c839e7e0275db23f68a6b85ecd9.png
-
懒汉式-静态内部类方式
静态内部类单例模式中实例由内部类创建,由于 JVM 在加载外部类的过程中, 是不会加载静态内部类的, 只有内部类的属性/方法被调用时才会被加载, 并初始化其静态属性。静态属性由于被 static
修饰,保证只被实例化一次,并且严格保证实例化顺序。
/**
* 静态内部类方式
*/
public class Singleton {
//私有构造方法
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
//对外提供静态方法获取该对象
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
-
枚举方式
枚举类实现单例模式是极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式。
/** * 枚举方式 */ public enum EnumSingleton { SINGLETON(1); private int a; EnumSingleton(int data){ this.a=data; } public static EnumSingleton getInstance(){ return EnumSingleton.SINGLETON;} } //枚举方式属于恶汉式方式。
-
问题
- 序列与反序列化
public class Test {
public static void main(String[] args) throws Exception {
//往文件中写对象
//writeObject2File();
//从文件中读取对象
Singleton s1 = readObjectFromFile();
Singleton s2 = readObjectFromFile();
//判断两个反序列化后的对象是否是同一个对象
System.out.println(s1 == s2); //返回false,破坏了
}
private static Singleton readObjectFromFile() throws Exception {
//创建对象输入流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Think\\Desktop\\a.txt"));
//第一个读取Singleton对象
Singleton instance = (Singleton) ois.readObject();
return instance;
}
public static void writeObject2File() throws Exception {
//获取Singleton类的对象
Singleton instance = Singleton.getInstance();
//创建对象输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Think\\Desktop\\a.txt"));
//将instance对象写出到文件中
oos.writeObject(instance);
}
}
- 反射
public class Test {
public static void main(String[] args) throws Exception {
//获取Singleton类的字节码对象
Class clazz = Singleton.class;
//获取Singleton类的私有无参构造方法对象
Constructor constructor = clazz.getDeclaredConstructor();
//取消访问检查
constructor.setAccessible(true);
//创建Singleton类的对象s1
Singleton s1 = (Singleton) constructor.newInstance();
//创建Singleton类的对象s2
Singleton s2 = (Singleton) constructor.newInstance();
//判断通过反射创建的两个Singleton对象是否是同一个对象
System.out.println(s1 == s2); //return false
}
}
- 解决办法
-
序列化、反序列方式破坏单例模式的解决方法
在Singleton类中添加
readResolve()
方法,在反序列化时被反射调用,如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象。Singleton类:
private Object readResolve() { //具体原理涉及到ObjectInputStream类的源码 return SingletonHolder.INSTANCE; }
-
反射方式破解单例的解决方法
//私有构造方法 private Singleton() { /* 反射破解单例模式需要添加的代码 */ synchronized(Singleton.class){ if(instance != null) { throw new RuntimeException("不能创建多个对象"); } } }
3.2 工厂模式
3.2.1 简单工厂模式:
用来生产同一等级架构中的任意产品(对于增加新的产品,需要修改已有代码).在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例。
//创建工厂
public class PhoneFactory {
public static Phone getPhone(String phone){
if(phone.equals("华为")){
return new HuaWei();
}else if(phone.equals("小米")){
return new XiaoMi();
}else {
return null;
}
}
}
//测试类
public class Consumer {
public static void main(String[] args) {
Phone p1= PhoneFactory.getPhone("华为");
Phone p2= PhoneFactory.getPhone("小米");
p1.name();
p2.name();
}
}
违反开闭原则
3.2.2 工厂模式:
用来生产同一等级架构中的固定产品,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。(支持增加任意产品)
//创建手机接口
public interface Phone {
void name();
}
//创建华为实现类
public class HuaWei implements Phone{
@Override
public void name() {
System.out.println("华为手机");
}
}
//创建手机工厂接口
public interface PhoneFactory {
Phone getPhone();
}
//创建华为工厂
public class HuaWeiFactory implements PhoneFactory{
@Override
public Phone getPhone() {
return new HuaWei();
}
}
//测试类
public class Consumer {
public static void main(String[] args) {
Phone phone = new HuaWeiFactory().getPhone();
phone.name();
}
}
一类工厂只生产一类具象
3.2.3 抽象工厂模式:
抽象工厂模式一个工厂生产多个抽象产品。具体产品同具体工厂之间是多对一的关系。(产品族)
//电脑接口
public interface Computer {
void play();
}
//创建华为电脑对象
public class HuaWeiComputer implements Computer{
@Override
public void play() {
System.out.println("HuaWei's play!");
}
}
//手机接口
public interface Phone {
void send();
}
//创建华为手机对象
public class HuaWeiPhone implements Phone{
@Override
public void send() {
System.out.println("HuaWei's send");
}
}
//抽象工厂
public interface IProductFactory {
//生产手机
Phone phone();
//生产电脑
Computer computer();
}
//创建华为工厂
public class HuaWeiFactory implements IProductFactory{
@Override
public Phone phone() {
return new HuaWeiPhone();
}
@Override
public Computer computer() {
return new HuaWeiComputer();
}
}
//测试类
public class Consumer {
public static void main(String[] args) {
HuaWeiFactory huaWeiFactory = new HuaWeiFactory();
Phone phone = huaWeiFactory.phone();
phone.call();
phone.send();
Computer computer = huaWeiFactory.computer();
computer.play();
computer.watch();
}
}
3.3 代理模式
3.3.1 静态代理
代理类继承要代理的接口并且包含具体类实例
//租房
public interface Rent {
void rent();
}
//房东
public class Master implements Rent{
@Override
public void rent() {
System.out.println("Master rent");
}
}
//中介
public class Proxy implements Rent{
private Master master;
public Proxy() {
}
public Proxy(Master master) {
this.master = master;
}
@Override
public void rent() {
see();
master.rent();
fare();
}
//看房
public void see(){
System.out.println("see");
}
//收费
public void fare(){
System.out.println("fare");
}
}
//测试类
public class Consumer {
public static void main(String[] args) {
Master master = new Master();
//进行代理
Proxy proxy = new Proxy(master);
//不需要通过对象,直接通过代理完成响应业务
proxy.rent();
}
}
,但是每有一个真实角色就会产生一个代理,代码量翻倍过于臃肿
3.3.12 动态代理
public class ProxyInvocationHandler implements InvocationHandler {
//被代理接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//得到代理类
public Object getProxy() {
return Proxy.newProxyInstance(getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
public void log(String s) {
System.out.println("[debug]:" + s);
}
//得到代理类
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
ProxyInvocationHandler handler = new ProxyInvocationHandler();
//设置代理对象
handler.setTarget(userService);
//生成代理类
IUserService proxy = (IUserService)handler.getProxy();
proxy.add();
proxy.query();
}
}
/**
1. 自定义InvocationHandler(中介类)重写invoke()(增强方法)
2. 通过新建的handler创建代理对象
1. 创建一个与代理对象相关联的InvocationHandler
InvocationHandler handler = new MyInvocationHandler<UserService>(userService);
2. 创建一个代理对象stuProxy,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
UserService proxy= (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class<?>[]{UserService.class}, handler);
第二步具体为 通过前两个参数生成动态代理类,再通过其包含中介类的构造方法和第三个handler参数创建代理类
使用Proxy类的getProxyClass静态方法生成一个动态代理类stuProxyClass
Class<?> proxyClass = Proxy.getProxyClass(UserService.class.getClassLoader(), new Class<?>[] {UserService.class});
获得proxyClass 中一个带InvocationHandler参数的构造器constructor
Constructor<?> constructor = Proxy.getConstructor(InvocationHandler.class);
通过构造器constructor来创建一个动态实例stuProxy
UserService proxy = (UserService) cons.newInstance(handler);
3.调用时
生成的该代理类对应方法为调用handler.invoke()方法
this.h.invoke(this, m3, null);
*/
3.4 装饰器模式
动态的将新功能附加到对象上 与静态代理区别在新字上
1、抽象构件:定义一个抽象接口以规范准备接收附加责任的对象。
2、具体构件角色
3、抽象装饰角色:继承抽象构件,并包含具体构件的实例。
4、具体装饰角色:实现抽象装饰的相关方法。
//定义抽象类
public abstract class Drink {
public abstract double cost();
}
//定义两个抽象类的实现类
public class Juice extends Drink{
@Override
public double cost() {
System.out.println("juice: "+16);
return 16;
}
}
public class Milk extends Drink{
@Override
public double cost() {
System.out.println("milk: "+12);
return 12;
}
}
//定义装饰抽象类
public abstract class Decorator extends Drink {
private Drink drink;
public abstract double cost();
}
//定义装饰具体实现类
public class Pudding extends Decorator{
private final static double COST = 5;
private Drink drink;
public Pudding(Drink drink) {
this.drink = drink;
}
@Override
public double cost() {
System.out.println("pudding:"+5);
return COST+drink.cost();
}
}
//测试类
public class Test {
public static void main(String[] args) {
Drink milk = new Milk();
milk = new Pudding(milk);
System.out.println(milk.cost());
}
}
3.5 适配器模式
适配器模式有两种实现方式:类适配器和对象适配器。
类适配器使用继承关系来实现,对象适配器使用组合关系来实现。
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/6f852ea44747fe5d47e910ad33371608.png)
// 类适配器: 基于继承
public interface ITarget {
void f1();
}
public class Adaptee {
public void fa() { }
}
public class Adaptor extends Adaptee implements ITarget {
public void f1() {
super.fa();
}
}
// 对象适配器:基于组合
public interface ITarget {
void f1();
}
public class Adaptee {
public void fa() { }
}
public class Adaptor implements ITarget {
private Adaptee adaptee;
public Adaptor(Adaptee adaptee) {
this.adaptee = adaptee;
}
public void f1() {
adaptee.fa(); //委托给Adaptee
}
}
- 适配器模式和装饰者模式:装饰者模式是对以前的类进行进一步的封装与增强,目的是一层一层的增加功能,而适配器模式主要是为了协调多个调用者不同的调用方式而设计出来的。
3.6 观察者模式
又称作发布-订阅模式
发布者内聚了观察者列表
//定义观察者接口
public interface Observer {
void response();
}
//具体化观察者1
public class Observer1 implements Observer{
@Override
public void response() {
System.out.println("Observer1 action");
}
}
//具体化观察者2
public class Observer2 implements Observer{
@Override
public void response() {
System.out.println("Observer2 action");
}
}
//抽象目标
public abstract class Subject {
//创建观察者集合
protected ArrayList<Observer> array = new ArrayList<Observer>();
//增加观察者
public void add(Observer observer){
array.add(observer);
}
//删除观察者
public void remove(Observer observer){
array.remove(observer);
}
//通知观察者方法
public abstract void notifyObserver();
}
//具体化目标
public class Subject1 extends Subject{
@Override
public void notifyObserver() {
for (Observer observer :array){
observer.response();
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
Subject subject = new Subject1();
Observer obs1 = new Observer1();
Observer obs2 = new Observer2();
subject.add(obs1);
subject.add(obs2);
subject.notifyObserver();
}
}
3.7责任链模式
一种处理请求的模式,它让多个处理器都有机会处理该诘求,直到其中某个处理成功为止。责任链模式把多个处理器串成链,然后让请求在链上传递。
//抽象处理者
public abstract class Handler {
private Handler next;
public void setNext(Handler next) { this.next = next; }
public Handler getNext() { return next; }
//处理请求
public abstract void handleRequest(int info);
}
//具体处理者1
public class Handler1 extends Handler{
@Override
public void handleRequest(int info) {
if (info <10){
System.out.println("Handler1完成处理");
}else {
if (getNext()!=null){
getNext().handleRequest(info);
}else {
System.out.println("没有处理者进行处理");
}
}
}
}
//具体处理者2
public class Handler2 extends Handler{
@Override
public void handleRequest(int info) {
if (info <20&&info>10){
System.out.println("Handler2完成处理");
}else {
if (getNext()!=null){
getNext().handleRequest(info);
}else {
System.out.println("没有处理者进行处理");
}
}
}
}
测试类
public class Test {
public static void main(String[] args) {
Handler handler1 = new Handler1();
Handler handler2 = new Handler2();
handler1.setNext(handler2);
handler1.handleRequest(5);
handler1.handleRequest(15);
handler1.handleRequest(25);
}
}
3.8 模板模式
public abstract class Travel {
public void travel() {
//做攻略
makePlan();
//收拾行李
packUp();
//去目的地
toDestination();
//玩耍、拍照
play();
//乘坐交通工具去返回
backHome();
}
protected abstract void makePlan();
protected abstract void packUp();
protected abstract void toDestination();
protected abstract void play();
protected abstract void backHome();
}
HashMap我们都很熟悉,可以通过put方法存元素,并且在元素添加成功之后,会调用的afterNodeInsertion方法。
在Spring中,ApplicationContext在使用之前需要调用的refresh方法
3.9 策略模式
public void notifyMessage(User user, String content, int notifyType) {
if (notifyType == 0) {
//调用短信通知的api发送短信
} else if (notifyType == 1) {
//调用app通知的api发送消息
}
}
public interface MessageNotifier {
/**
* @param notifyType 0:短信 1:app
* @return
*/
boolean support(int notifyType);
void notify(User user, String content);
}
@Component
public class SMSMessageNotifier implements MessageNotifier {
@Override
public boolean support(int notifyType) {
return notifyType == 0;
}
@Override
public void notify(User user, String content) {
//调用短信通知的api发送短信
}
}
@Resource
private List<MessageNotifier> messageNotifiers;
public void notifyMessage(User user, String content, int notifyType) {
for (MessageNotifier messageNotifier : messageNotifiers) {
if (messageNotifier.support(notifyType)) {
messageNotifier.notify(user, content);
}
}
}