1 什么是设计模式
1.1概述
比如,MVC模式
模式——经验
模式是软件开发实践过程中总结的经验。 代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。
设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
所以,设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。
1.2设计模式需要注意的地方
城市辐射农村,可以理解为设计模式由技术大牛提出来后,由于好用普通的程序员开始使用。
设计模式不要乱用,要结合实际情况,比如有些情况下不使用设计模式就可以保证程序的健壮性和可靠性,此时就不需要使用设计模式。
设计模式不是万能的,设计模式并不能够解决实际业务中的问题。
1.3优点
•代码质量好 可靠性高。
•可复用代码,代码可重用性高。
•代码更规范,更容易被人理解。
2 软件设计的原则
2.1什么是软件设计?
设计所要解决的主要问题,是如何高效率、高质量、低风险的应对各种各类变化,例如需求变更、软件升级等。
2.2设计模式的五个原则solid
- S - 单一职责原则: Single
- O - 开放封闭原则: open,对增加开放,对修改封闭
- L - Liskov原则: 里氏替代原则
- I - 接口隔离原则: isolation
- D – 依赖倒置原则: 接口接收实例,依赖倒置,比如UserDao userDao = new UserDaoImpl();
2.3 S - 单一职责原则
一个模块负责一个功能
一个类负责一个业务
一个API去实现一个功能
不同的功能分隔开。一个类如果需要变化,最好只能有一个维度的原因。
2.4 O - 开放封闭原则:
对什么开放:对扩展开放
扩展:新功能可以(提倡)去增加代码(增加一个类)
项目不断迭代(增加新的需求)
对什么封闭:对修改封闭
不建议去修改代码 (尤其是一些底层的API)
牵一发而动全身。
2.5 L - Liskov原则:
里氏替换原则:任何一个基类可以出现的地方,子类一定可以出现。(用父类接受子类的实例)
//写法1
UserDao dao = new UserDaoImpl2();
dao.findUserByid();
dao.findUserByName();
//写法2
UserDaoImpl2 dao = new UserDaoImpl2 ();
dao.findUserByid();
dao.findUserByName();
2.6 I - 接口隔离原则:
接口:interface (功能)
隔离:分开
不同功能的接口,最好放在不同的interface里。
一个接口里最好不要集成太多的功能,
否则用户去实现该接口会比较麻烦。
2.7 D – 依赖倒置原则:(Spring DI IOC APSPECTJ)
具体依赖于抽象,而非抽象依赖于具体。
先去设计抽象的DAO (接口)
3 常见的设计模式
3.1单例模式(Singleton Pattern)
3.1.1概念
•何为单例?单一实例。全局只有一个。
•哪些场合需要用到单例?
Servlet
ServletContext
3.1.2特点
1、构造方法私有
2、构造方法私有单例类必须给所有其他对象提供这一实例,即提供一个方法给其他方法调用
3、包含自己类型的成员变量
3.1.3入门案例
3.1.3.1实现1: 线程不安全的懒加载
这种单例 好不好。
有没有什么问题?
某些情况下,可能会有多个实例出现。
多线程:因为线程切换,可能会造成出现两个实例。
3.1.3.2实现2 线程安全的懒加载
优点:多线程下依然是单例
缺点:加锁会影响执行效率。
3.1.3.3实现3 线程安全的立即加载
不使用锁,让类在加载的时候自行实例化。
3.1.3.4实现4 (实现3的变种)
3.1.3.5实现5 线程安全的懒加载——静态内部类的实现
内部类调用关系
3.2工厂模式(Factory Pattern)
3.2.1概念
生成产品的工厂,生产对象(实例)
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。 将创建对象的具体逻辑隐藏起来。
Animal animal = AnimalFactory.create();
3.2.2简单工厂
给我需要使用的类的名字。简单工厂返回一个这种类实例。
如果需要生产新的小动物。需要在原有的方法内修改代码,违反开闭原则。
3.2.3工厂方法模式
工厂方法模式,创建一个工厂接口和创建多个工厂实现类 具体的创建由工程的实现类完成。
完全遵从开闭原则,不对任何已有代码进行修改,直接新增一个类。
注意:maven父子工程可能会导致新增依赖识别不了,尽量新建同级的module。
3.3代理模式(Proxy Pattern)(重要)
3.3.1代理
为某个对象提供一个代理对象以控制对这个对象的访问。
代理对象:中介
代理类 负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
被代理对象(委托类):业主。
3.3.2代理的设计模式有何优点
可以在目标对象实现的基础上,增强额外的功能,扩展目标对象的功能。
3.3.3静态代理
需要自己提供代理类
3.3.4动态代理
动态代理它可以直接给某一个目标对象生成一个代理对象,而不需要代理类存在。
Java动态代理
•Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。
3.3.4.1动态代理的实现方式
1.jdk提供一个Proxy类可以 实现接口类的对象直接生成代理对象。(被代理类要有一个接口的实现)
2.cglib
3.3.4.2JDK 的动态代理
被代理类,被增强的类 要有接口的实现 → 生成的代理对象的类型是和接口相关
public class JdkProxyTest {
@Test
public void mytest1(){
HouseOwner houseOwner = new HouseOwner();
//获得动态代理对象
//返回值不能够用实现类接收,用接口来接收
RentHouse houseProxy = (RentHouse) Proxy.newProxyInstance(houseOwner.getClass().getClassLoader(),
houseOwner.getClass().getInterfaces(),
new InvocationHandler() {
//当你使用代理对象去调用方法的时候,就会进入到invoke
//invoke三个参数分别代表什么
//1、proxy proxy对象
//2、method proxy对象正在调用的方法
//3、args → method对应的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("你正在调用的方法是 :" + method.getName());
if ("rentHouse".equals(method.getName())){
args[0] = (int)args[0] - 500;
System.out.println("房东能够收到的money = " + args[0]);
}
//一定要传入的是被代理的这个类的对象,而不是代理对象
//如果传入的是代理对象,会出现死循环,最后溢出
Object invoke = method.invoke(houseOwner, args);
System.out.println("一起哈啤");
return invoke;
}
});
//在代理对象上调用方法就会执行上面的invoke方法,分别传入三个参数
houseProxy.rentHouse(2000);
}
}
两个bean
public interface RentHouse {
public boolean rentHouse(int money);
}
public class HouseOwner implements RentHouse{
@Override
public boolean rentHouse(int money) {
System.out.println("房东租房子的真实money 为 " + money);
if (money >= 1500)
{
return true;
}
return false;
}
}
3.3.4.3 cglib 的动态代理
继承实现的 代理对象 → 继承了被代理类
引入依赖 → cglib
public class CglibProxyTest {
@Test
public void mytest(){
HouseOwner houseOwner = new HouseOwner();
//仍然是要获得代理对象 → 通过cglib来获得
//代理对象是被代理类的子类的对象
HouseOwner houseProxy = (HouseOwner) Enhancer.create(HouseOwner.class, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("有人说你野心勃勃");
long start = System.currentTimeMillis();
Object invoke = method.invoke(houseOwner, args);
System.out.println("有人爱你灵魂有火");
long end = System.currentTimeMillis();
System.out.println(method.getName() + "方法执行的时间为" + (end - start));
return invoke;
}
});
houseProxy.rentHouse(1500);
}
}
3.4建造者设计模式
类似于工厂模式,但更侧重于创建之前的参数设计。
3.4.1返回值为void
3.4.2返回值为builder类