设计模式(狂神说)
**视频地址:**https://www.bilibili.com/video/BV1mc411h719/
设计模式是一种思想,是内功!
1、设计模式概述
设计模式:是一套用来提高代码可复用性,可维护性、可读性、稳健型以及安全性的解决方案
设计模式的本质:是面向对象设计原则的实际运用,是对类的封装、继承、多态以及类的关联关系和组合关系的充分理解。
设计模式的的基本要素:模式名称、问题、解决方案、效果
设计模式是一种思维,一种态度,一种进步!
分类
创建型模式:(描述怎样去创建一个对象,创建和使用分离)
结构型模式:(描述如何将类或对象安装某种类型组成更大的结构)
行为型模式:(描述类和对象如何可以相互协作)
2、OOP七大原则
**开闭原则:**对扩展开放,对修改关闭(当需求需要改变的时候,尽量去扩展)
里氏替换原则: 继承必须确保超类所拥有的性质在子类中仍然成立(尽量不重写父类的方法)
依赖倒置原则: 要面向接口编程,不要面向实现编程
单一职责原则: 控制类的粒度大小,将对象解耦,提高其内聚性(一个对象不应该担任太多的职责,原子性,单一的方法做单一的事情)
接口隔离原则: 要为各个类建立他们需要的专用接口
迪米特法则: 只与你的直接朋友交谈,不跟“陌生人”说话,降低代码之间的耦合度
合成复用原则: 尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
3、单例模式
单例模式是指在内存中只会创建且仅创建一次对象的设计模式。
在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。
3.1、饿汉式
由于Hungry中对象在开始就被创造,而此时可能并不需要这个对象,其属性就可能会浪费较大空间
//饿汉式单例
public class Hungry {
//可能会浪费空间
private byte[] data1 = new byte[1024*1024];
private byte[] data2 = new byte[1024*1024];
private byte[] data3 = new byte[1024*1024];
private byte[] data4 = new byte[1024*1024];
//构造器私有
private Hungry(){
}
private final static Hungry Hungry = new Hungry();
public static Hungry getInstance (){
return Hungry;
}
}
3.2、DCL懒汉式
在我们需要的时候再去创建对象,如果此时单例对象已经存在,就直接返回,否则创建对象并返回。
//懒汉式单例
public class Lazy {
private Lazy(){
}
private static Lazy lazy;
public static Lazy getInstance(){
if (lazy == null){
lazy = new Lazy();
}
return lazy;
}
}
这样的代码在单线程下是没有问题的,但是多线程会出现问题!
多线程测试:
//懒汉式单例
public class Lazy {
private Lazy(){
System.out.println(Thread.currentThread().getName() + "--->OK");
}
private static Lazy lazy;
public static Lazy getInstance(){
if (lazy == null){
lazy = new Lazy();
}
return lazy;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
Lazy.getInstance();
}).start();
}
}
}
在构造器中增加输出当前线程名的代码,这样构造了几次就会输出几个线程
为了保证线程的安全性,我们要给getInstance方法加锁
由于new对象的过程不是一个原子性操作
3.3、静态内部类
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
3.4、拓展
但是以上这些都是不安全的!因为有反射!
三重检测!
标志位加密~
道高一尺,魔高一丈。这样依然是可以破解的~
public static void main(String[] args) throws Exception {
Field field = Lazy.class.getDeclaredField("flag");
field.setAccessible(true);
Constructor<Lazy> constructor = Lazy.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
Lazy lazy2 = constructor.newInstance();
field.set(lazy2,false);
Lazy lazy3 = constructor.newInstance();
System.out.println(lazy2 == lazy3);
}
3.5、枚举
// enum 本身也是一个 class 类
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
EnumSingle instance1 = EnumSingle.INSTANCE;
//Constructor<EnumSingle> declaredConstructor=EnumSingle.class.getDeclaredConstructor(null);
// 枚举没有无参构造,只有有参构造
Constructor<EnumSingle> declaredConstructor=EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
// java.lang.NoSuchMethodException: com.zwj.singletonpattern.EnumSingle <init> 没有空参的构造方法
// java.lang.IllegalArgumentException: Cannot reflectively create enum objects 反射不能破坏枚举的单例
System.out.println(instance1);
System.out.println(instance2);
}
}
枚举没有无参构造,只有有参构造
4、工厂模式
工厂模式将目的将创建对象的具体过程屏蔽隔离起来,从而达到更高的灵活性。
4.1、简单工厂模式
Car接口
public interface Car {
void name();
}
五菱宏光实现类
public class WuLing implements Car {
@Override
public void name() {
System.out.println("五菱宏光");
}
}
特斯拉实现类
public class Tesla implements Car {
@Override
public void name() {
System.out.println("特斯拉");
}
}
汽车工厂
public class CarFactory {
public static Car getCar(String car){
if (car.equals("五菱")){
return new WuLing();
} else if (car.equals("特斯拉")){
return new Tesla();
} else {
return null;
}
}
}
消费者
public class Consumer {
public static void main(String[] args) {
/*
原本的方式,我们需要知道接口的实现类
想要什么车就需要new什么车
*/
Car car1 = new WuLing();
Car car2 = new Tesla();
car1.name();
car2.name();
/*
新的方式,我们只需要关心想要什么车
不需要知道这个车是怎么生成的
*/
Car car3 = CarFactory.getCar("五菱");
car3.name();
Car car4 = CarFactory.getCar("特斯拉");
car4.name();
}
}
原来的方式,用户需要知道都有什么具体的车,需要什么车就去new什么车,有可能需要一堆参数,客户需要对实现类有比较深入的了解,现在的方式,客户只需要面向车工厂,告诉车工厂我们要什么车即可!
但是这样也有不好的地方,如果此时来了一个新的汽车,比如大众,我们就需要去CarFactory类中进行增加代码!不满足开闭原则!
因此简单工厂模式也称为静态工厂模式,如果有新的产品,不进行修改代码是不可以完善的!
4.2、工厂方法模式
给每一种Car一个工厂!
public interface CarFactory {
Car getCar();
}
让每一种车的工厂去实现车工厂这个接口
public class TeslaFactory implements CarFactory {
@Override
public Car getCar() {
return new Tesla();
}
}
public class WuLingFactory implements CarFactory {
@Override
public Car getCar() {
return new WuLing();
}
}
这个时候如果我们需要拓展,只需要增加新的车的类以及新的车的工厂类即可,并没有修改原来的代码
每多一种车,我们就需要多一个汽车的实现类+一个汽车工厂的实现类,代码量会变得很大!
但是我们的拓展是横向的,不需要修改原有代码
应用场景:
-
JDK中Calendar的getInstance方法
-
JDBC中的Connection对象的获取
-
Spring中lOC容器创建管理bean对象
-
反射中Class对象的newlnstance方法
5、抽象工厂模式
产品等级结构和产品族:
首先定义路由器和手机的接口
//路由器产品接口
public interface IRouterProduct {
void start();
void shutdown();
void openWifi();
void setting();
}
//手机产品接口
public interface IphoneProduct {
void start();
void shutdown();
void callUp();
void sendSMS();
}
定义华为的路由器和手机
//华为手机
public class HuaWeiPhone implements IphoneProduct{
@Override
public void start() {
System.out.println("华为手机开机");
}
@Override
public void shutdown() {
System.out.println("华为手机关机");
}
@Override
public void callUp() {
System.out.println("华为手机打电话");
}
@Override
public void sendSMS() {
System.out.println("华为手机发短信");
}
}
//华为路由器
public class HuaWeiRouter implements IRouterProduct{
@Override
public void start() {
System.out.println("华为路由器开机");
}
@Override
public void shutdown() {
System.out.println("华为路由器关机");
}
@Override
public void openWifi() {
System.out.println("华为路由器打开wifi");
}
@Override
public void setting() {
System.out.println("华为路由器设置");
}
}
接下来定义小米的路由器和手机
…
接下来我们定义抽象工厂
//抽象产品工厂
public interface IProductFactory {
//生产手机
IphoneProduct iphoneProduct();
//生产路由器
IRouterProduct routerProduct();
}
而华为和小米都能生产手机和路由器,于是就有了华为厂家和小米厂家
//华为工厂
public class HuaWeiFactory implements IProductFactory {
@Override
public IphoneProduct iphoneProduct() {
return new HuaWeiPhone();
}
@Override
public IRouterProduct routerProduct() {
return new HuaWeiRouter();
}
}
//小米工厂
public class XiaoMiFactory implements IProductFactory {
@Override
public IphoneProduct iphoneProduct() {
return new XiaoMiPhone();
}
@Override
public IRouterProduct routerProduct() {
return new XiaoMiRouter();
}
}
测试
public class Client {
public static void main(String[] args) {
System.out.println("小米系列产品-----------------------");
XiaoMiFactory xiaoMiFactory = new XiaoMiFactory();
IphoneProduct iphoneProduct = xiaoMiFactory.iphoneProduct();
iphoneProduct.start();
iphoneProduct.sendSMS();
IRouterProduct routerProduct = xiaoMiFactory.routerProduct();
routerProduct.start();
routerProduct.openWifi();
System.out.println("小米系列产品-----------------------");
}
}
缺点:
- 产品簇中增加新的产品很困难
- 增加了系统的抽象性和理解难度
6、建造者模式
public abstract class Builder {
abstract Builder buildA(String msg);
abstract Builder buildB(String msg);
abstract Builder buildC(String msg);
abstract Builder buildD(String msg);
abstract Product getProduct();
}
public class Product {
private String buildA = "汉堡";
private String buildB = "可乐";
private String buildC = "薯条";
private String buildD = "甜点";
public String getBuildA() {
return buildA;
}
public void setBuildA(String buildA) {
this.buildA = buildA;
}
public String getBuildB() {
return buildB;
}
public void setBuildB(String buildB) {
this.buildB = buildB;
}
public String getBuildC() {
return buildC;
}
public void setBuildC(String buildC) {
this.buildC = buildC;
}
public String getBuildD() {
return buildD;
}
public void setBuildD(String buildD) {
this.buildD = buildD;
}
@Override
public String toString() {
return "Product{" +
"buildA='" + buildA + '\'' +
", buildB='" + buildB + '\'' +
", buildC='" + buildC + '\'' +
", buildD='" + buildD + '\'' +
'}';
}
}
public class Worker extends Builder {
private Product product;
public Worker(){
product = new Product();
}
@Override
Builder buildA(String msg) {
product.setBuildA(msg);
return this;
}
@Override
Builder buildB(String msg) {
product.setBuildB(msg);
return this;
}
@Override
Builder buildC(String msg) {
product.setBuildC(msg);
return this;
}
@Override
Builder buildD(String msg) {
product.setBuildD(msg);
return this;
}
@Override
Product getProduct() {
return product;
}
}
public class Test {
public static void main(String[] args) {
Worker worker = new Worker();
Product product = worker
.buildA("全家桶")
.buildB("雪碧")
.getProduct();
System.out.println(product.toString());
}
}
7、原型模式
如果一个对象的创建非常复杂,假如我们又需要创建一个相同的这样的对象,就可以使用原型模式
- 实现一个接口
- 重写一个方法
public class Video implements Cloneable {
private String name;
private Date createTime;
public Video() {
}
public Video(String name, Date createTime) {
this.name = name;
this.createTime = createTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return "Video{" +
"name='" + name + '\'' +
", createTime=" + createTime +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class BiliBili {
public static void main(String[] args) throws CloneNotSupportedException {
Date date = new Date();
Video v1 = new Video("狂神说JAVA",date);
//原始方法
Video video = new Video();
video = v1;
//浅拷贝
Video v2 = (Video)v1.clone();
System.out.println("v1=>" + v1);
System.out.println("v2=>" + v2);
//修改date的值
date.setTime(22222);
System.out.println("v1=>" + v1);
System.out.println("v2=>" + v2);
}
}
原因是clone方法默认是浅拷贝
实现深拷贝
- 重写clone方法,把createTime这个对象属性也给clone
@Override
protected Object clone() throws CloneNotSupportedException {
Video v = (Video)super.clone();
v.createTime = (Date) this.createTime.clone();
return v;
}
通过序列化和反序列化也可以实现深拷贝
Spring中Bean的创建就用到了单例模式或原型模式!
8、适配器模式
public class Adaptee {
public void goToInternet(){
System.out.println("我要上网");
}
}
public class Adapter implements NetToUSB {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void Net() {
adaptee.goToInternet();
}
}
public interface NetToUSB {
void Net();
}
public class Computer {
public void Nat(Adapter adapter){
adapter.Net();
}
public static void main(String[] args) {
Computer computer = new Computer(); //电脑
Adaptee adaptee = new Adaptee(); //网线
Adapter adapter = new Adapter(adaptee); //适配器
computer.Nat(adapter);
}
}
这里很像SpringBoot里的service调mapper层同时结合了Spring的IOC
适配器模式就是将两个没有关系的类结合起来
9、桥接模式
public interface Brand {
void info();
}
public class Lenovo implements Brand {
@Override
public void info() {
System.out.print("联想");
}
}
public class Apple implements Brand {
@Override
public void info() {
System.out.print("苹果");
}
}
public abstract class Computer {
protected Brand brand;
public Computer(Brand brand) {
this.brand = brand;
}
public void info(){
brand.info();
}
}
class DeskTop extends Computer{
public DeskTop(Brand brand) {
super(brand);
}
@Override
public void info() {
super.info();
System.out.print("台式机");
}
}
class LapTop extends Computer{
public LapTop(Brand brand) {
super(brand);
}
@Override
public void info() {
super.info();
System.out.print("笔记本");
}
}
public class Test {
public static void main(String[] args) {
Computer computer = new LapTop(new Lenovo());
computer.info();
System.out.println();
Computer computer2 = new DeskTop(new Apple());
computer2.info();
}
}
电脑里自带品牌,客户可以自己组装想要的产品
10、静态代理模式
为什么要学习代理模式,因为这就是SpringAOP的底层。
从这里开始是狂神老师Spring5课程的内容,最好对Spring有了解!
代理模式的分类:
- 静态代理
- 动态代理
静态代理:
接口:
public interface Rent {
void rent();
}
真实角色:
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子~~~");
}
}
代理角色:
public class Proxy implements Rent{
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
host.rent();
seeHouse();
hetong();
fare();
}
public void seeHouse(){
System.out.println("中介带你看房~~~");
}
public void fare(){
System.out.println("中介收中介费~~~");
}
public void hetong(){
System.out.println("中介签合同~~~");
}
}
客户访问代理角色:
public class Client {
public static void main(String[] args) {
Host host = new Host();
//代理房东
Proxy proxy = new Proxy(host);
proxy.rent();
}
}
11、动态代理模式
动态代理和静态代理角色一样,动态代理底层是反射机制
动态代理类是动态生成的,不是我们直接写好的!
动态代理(两大类):基于接口,基于类
-
基于接口:JDK的动态代理
-
基于类:cglib
-
java字节码实现:javassist
了解两个类
1、Proxy:代理
2、InvocationHandler:调用处理程序
抽象角色:
public interface Host {
public void rent();
}
真实角色:
public class HostMaster implements Host{
public void rent() {
System.out.println("房东要租房子");
}
}
代理角色:
//我们会用这个类,自动生成代理类!
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质,就是使用反射机制实现!
Object result = method.invoke(rent, args);
seeHose();
fee();
return result;
}
public void seeHose(){
System.out.println("中介带着看房子!");
}
public void fee(){
System.out.println("中介收取费用!");
}
}
用户类:
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色:现在没有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象!
pih.setRent(host);
Rent proxy = (Rent) pih.getProxy(); //这里的proxy就是动态生成的,我们并没有写
proxy.rent();
}
}
在此,我们可以提炼出ProxyInvocationHandler作为工具类
//用这个类自动生成代理类!
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
public void log(String msg){
System.out.println("[Debug] 使用了一个"+msg+"方法");
}
}
代理模式的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共角色就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可!