软件工程与体系结构
设计模式
六大原则
原则 | 解释 |
---|---|
单一原则 (SRP) | 一个类只做一件事 |
开放-封闭原则(OCP) | 软件实体(类、模块、函数)可以拓展,但是不可修改 |
依赖倒转原则(DIP) | A.高层模块不应该依赖底层,两个都应该依赖抽。B.抽象不应该依赖细节,细节依赖抽象 |
里氏代换原则(LSP) | 子类型必须能够替换掉它们的父类型 |
迪米特法则(LoD) | 如果两个类不必直接通信,那么这两个类不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可通过第三者发起这个调用 |
合成/聚合复用原则(CARP) | 尽量使用合成/聚合,尽量不要使用类继承 |
创建型模式
创建型模式有五种
- 工厂模式
- 抽象工厂模式
- 单例模式
- 构造者模式
- 原型模式
单例模式
定义:保证一个类只有一个实例,并且提供一个全局访问点
场景:重量级的对象,不需要多个实例,如线程池,数据库连接池
懒汉模式
定义:延迟加载,只有在真正使用的时候,才开始实例化
- 线程安全问题
- double check 加锁优化
- 编译器(JIT),CPU有可能对指令进行重排序,导致使用到尚未初始化的实例,可以通过添加volatile关键字进行修饰
对于volatile修饰的字段,可以防止指令重排
饿汉模式
定义:类加载的初始化阶段就完成了实例的初始化。本质上就是借助于jvm类加载机制,保证实例的唯一性
类加载过程:
- 加载二进制数据到内存中,生成对应的Class数据结构
- 连接:a.验证,b.准备(给类的静态成员变量赋默认值),c.解析
- 初始化:给类的静态变量赋初值
只有在真正使用对应的类时,才会触发初始化 如(当前类是启动类既main函数所在类,直接进行new操作,访问静态属性,访问静态方法,用反射访问类,初始化一个类的子类等)
工厂模式
定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类。使一个类的实例化延迟到其子类
适用:当一个类不知道它所创建的对象的类的时候
作用:实现了创建者和调用者的分离
详细分类:
- 简单工厂模式
- 用来生产同一等级结构中的任意产品(对于增加新的产品,需要扩展已有代码)
- 工厂方法模式
- 用来生产同一等级结构中的固定产品(支持增加任意产品)
- 抽象工厂模式
- 围绕一个超级工厂创建其他工厂,该超级工厂又称为其他工厂的工厂
类图:
核心本质:
- 实例化对象不适用new,用工厂方法代替
- 将选择实现类,创建对象统一管理和控制。从而将调用者跟我们的实现类解耦
简单工厂模式
简称静态工厂模式
- 不符合设计原则中的开闭原则,但是实际使用最多
- 要增加一个新的产品,如果不修改代码,根本做不到
工厂方法模式
- 不修改已有类的前提下,通过增加新的工厂类实现扩展
Car和CarFactory接口
WuLing和WuLingFactory
测试类
抽象工厂模式
定义:抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类
适用场景:
- 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
- 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量的重复代码
- 提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现
优点:
- 具体产品在应用层的代码隔离,无需关心创建的细节
- 将一个系列的产品统一到一起创建
缺点:
- 规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难;
- 增加了系统的抽象性和理解难度
类图:
结构型模式
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者采用组合或聚合来组合对象。
由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。
结构型模式分为以下7种:
- 代理模式
- 适配器模式
- 装饰者模式
- 桥接模式
- 外观模式
- 组合模式
- 享元模式
代理模式
定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时动态生成。动态代理又有JDK代理和CGLib代理两种。
分类:分为静态代理和动态代理
静态代理
角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
- 客户:访问代理对象的人
优点
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共业务就交给代理角色!实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
缺点
- ·一个真实角色就会产生一个代理角色;代码量会翻倍,开发效率会变低~
例子1:
代码步骤:
1、接口
//租房
public interface Rent {
//出租房屋
public void rent();
}
2、真实角色
//房东
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
3、代理角色
//代理角色
public class Proxy implements Rent{
private Host host;
public Proxy(){
}
public Proxy(Host host){
this.host = host;
}
//帮房东向别人租房子
@Override
public void rent() {
seeHouse();
host.rent(); //房东出租房子
HeTong();
fare();
}
//看房
public void seeHouse(){
System.out.println("中介代理带你去看房");
}
//签合同
public void HeTong(){
System.out.println("签合同");
}
//收中介费
public void fare(){
System.out.println("收中介费");
}
}
4、客户端访问代理角色
public class Client {
public static void main(String[] args) {
//房东要出租房子
Host host = new Host();
//代理,中介帮房东租房子,代理角色一般会有一些附属操作
Proxy proxy = new Proxy(host);
//你不用找房东租房子,直接找中介租房即可
proxy.rent();
}
}
结果:
例子2:火车站卖票
如果要买火车票的话,需要去火车站买票,坐车到火车站,排队等一系列的操作,显然比较麻烦。而火车站在多个地方都有代售点,我们去代售点买票就方便很多了。这个例子其实就是典型的代理模式,火车站是目标对象,代售点是代理对象。
类图:
//卖票接口
public interface SellTickets {
void sell();
}
//火车站 火车站具有卖票功能,所以需要实现SellTickets接口
public class TrainStation implements SellTickets {
public void sell() {
System.out.println("火车站卖票");
}
}
//代售点
public class ProxyPoint implements SellTickets {
private TrainStation station = new TrainStation();
public void sell() {
System.out.println("代理点收取一些服务费用");
station.sell();
}
}
//测试类
public class Client {
public static void main(String[] args) {
ProxyPoint pp = new ProxyPoint();
pp.sell();
}
}
从上面代码中可以看出测试类直接访问的是ProxyPoint类对象,也就是说ProxyPoint作为访问对象和目标对象的中介。同时也对sell方法进行了增强(代理点收取一些服务费用)。
c void sell() {
System.out.println(“代理点收取一些服务费用”);
station.sell();
}
}
//测试类
public class Client {
public static void main(String[] args) {
ProxyPoint pp = new ProxyPoint();
pp.sell();
}
}
从上面代码中可以看出测试类直接访问的是ProxyPoint类对象,也就是说ProxyPoint作为访问对象和目标对象的中介。同时也对sell方法进行了增强(代理点收取一些服务费用)。
> 注:此乃黄敢敢夜观各大网站视频所学即得笔记,如有雷同,请联系我进行删除!阿里嘎多