UML建模
统一建模语言(UML)是用来设计软件的可视化建模语言。它的语言特点是简单 统一 图形化 能表达软件设计中的动态与静态信息。
UML的分类
动态结构图: 类图 对象图 组件图 部署图
动态行为图: 状态图 活动图 时序图 协作图 构件图等
在UML类图中表示具体的类
在类的UML图中,使用长方形描述一个类的主要构成,长方形垂直地分为三层,以此放置类的名称、属性和方法。
在UML类图中表示抽象类
抽象类在UML类图中同样用矩形框表示,但是抽象类的类名以及抽象方法的名字都用斜体字表示,如图所示。
在UML类中表示接口
接口在类图中也是用矩形框表示,但是与类的表示法不同的是,接口在类图中的第一层顶端用构造型<<interface>>表示,下面是接口的名字,第二层是方法。
在UML类图中表示关系
类和类、类和接口、接口和接口之间存在一定关系,UML类图中一般会有连线指明它们之间的关系。
软件设计原则
1.开闭原则
定义:一个软件实体类 模块和函数应该对扩展开放,对修改关闭。
用抽象构建框架,用实现扩展细节
优点: 提高软件系统的可复用性及维护性
开闭原则是所有的设计模式的最核心的目标,顶层设计思维:
- 抽象意识
- 封装意识
- 扩展的意思
2.依赖倒置原则
定义: 高层模块不应该依赖底层模块,二者都应该依赖其抽象
抽象不应该依赖细节;细节应该依赖抽象
针对接口编程,不要针对实现编程
优点: 可以减少类间的耦合度 提高系统稳定性 提高代码可读性和可维护性, 可降低修改程序所造成的风险。
3.单一职责原则
定义: 不要存在多余一个导致类变更的原因
一个类/接口/方法只负责一项职责
优点: 降低类的复杂度 提高类的可读性,提高系统的可维护性,降低变更引起的风险
如何判断一个类的职责是否单一?
- 类中的代码行数 函数 或者属性过多
- 类依赖的其他的类过多
- 私有方法过多
- 类中的大量方法总是操作类中的几个属性
4.接口隔离原则
定义: 用多个专门的接口,而不使用单一的总接口,客户端不应该依赖它不需要的接口
一个类对一个类的依赖应该建立在最小的接口上
建立单一接口,不要建立庞大臃肿的接口
尽量细化接口,接口中的方法尽量少
注意适度原则,一定要适度
优点:符合我们常说的高内聚低耦合的设计思想
从而使得类具有很好的可读性,可扩展性和可维护性。
遵循接口隔离原则的优势
- 将臃肿接口分解成多个粒度晓得接口,可以提高系统的灵活性喝可维护性
- 使用多个专门的接口,还能体现出对象的层次
- 能顾减少项目工程中的冗余代码
5.迪米特法则
定义: 一个对象应该对其他对象保持最少的了解。又叫最少知道原则
尽量降低类与类之间的耦合
优点: 降低类之间的耦合
6.里氏替换原则
什么是替换
替换的前提是面向对象语言所支持的多态特性,同一个行为具有多个不同表现形式或形态的能力
简单说就是当我的一个方法的参数是一个接口类型时,这个方法可以接收所有实现过这个接口的实现类
什么是期望行为一致的替换
在不了解派生类的情况下,仅通过接口或基类的方法,即可清楚的知道方法的行为,而不管哪种派生类的实现,都与接口或基类方法的期望行为一致。
7.总结
单一职责(SRP)
- 概念:一个类只负责完成一个职责或功能
- 作用:1.提高类的内聚性 2.实现代码的高内聚 低耦合
- 不满足的4种情况: 类中的代码行数 函数 或者属性过多
类依赖的其他类过多
私有方法过多
类种大量的方法都是集中操作类中的几个属性
开闭原则(OCP):
概念: 对扩展开放,对修改关闭
开闭原则并不是说完全的杜绝修改,而是以最小的修改代码的代价来完成新功能的开发
作用:新逻辑解耦,需要发生改变不会影响老业务的逻辑
改动成本最小,只需要追加新逻辑,不需要改的老逻辑
提高代码的稳定性喝可扩展性
如何做到开闭原则:
锻炼顶层思维:扩展意识 抽象意识 封装意识
提高代码扩展性的方式:多态 依赖注入 面向接口编程 合理使用设计模式
里氏替换原则(LSP):子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的逻辑行为不变及正确性不被破坏
作用:为良好的继承定义了一个规范
提高代码的健壮性,降低程序出错的可能性
里氏替换原则与多态的区别:
多态是面向对象的一大特性,也是面向对象编程语言的一种语法。它是一种代码实现的思路。
里氏替换是一种设计原则,用来指导继承关系中子类该如何设计,在替换父类的时候,不改变原有程序的逻辑及不破坏原有程序的正确性。
接口隔离原则(ISP)
一个类对另一个类的依赖应该建立在最小的接口上,要为各个类建立它们需要的专用接口,而不要试图建立一个很庞大的接口供所有依赖它的类去调用。
作用:提高系统的灵活性喝可维护性
减少项目工程中的代码冗余
接口隔离原则与单一职责原则的区别:
单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。
单一职责原则主要是约束类,它针对的是程序中实现和细节;
接口隔离原则主要是约束接口,主要争对抽象和程序整体框架的构建。
依赖倒置原则(DIP)
概念: 高层模块不应该依赖于底层模块,二者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。
作用:
减少类间的耦合性,提高系统的稳定性
降低并行开发引起的风险
提高代码的可读性和可维护性
依赖倒置 控制反转 依赖注入 :
依赖倒置: 是一种通用的软件设计原则,主要用来指导框架层面的设计
控制反转: 与依赖倒置有一点相似,它也是一种框架设计常用的模式,但并不是具体的方法。
依赖注入: 是实现控制反转的一个手段,它是一种具体的编码技巧。
迪米特法则(LKP):
概念: 不该有直接依赖关系的类之间,不要有依赖,有依赖的类之间,尽量只依赖必要的接口。
作用:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发调用。其目的是降低类之间的耦合度,提高模块的相对独立性。
使用注意:过渡使用迪米特法则回使系统大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低。所以,在采用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。
创建型
简单工厂
适用场景: 工厂类负责创建的对象比较少
客户端(应用层)只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心
优点: 只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节
缺点:工厂类的职责相对过重,增加新的产品 需要修改工厂类的判断逻辑,违背了开闭原则。
/*** 简单工厂Demo*/
public class VideoFactor {//利用反射public Video getVideo(Class c){Video video = null;try{video=(Video) Class.forName(c.getName()).newInstance();} catch (ClassNotFoundException e) {throw new RuntimeException(e);} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);}return video;}// public Video getVideo(String type) {// if ("java".equals(type)) {// return new JavaVideo();// }else if ("python".equals(type)) {// return new PythonVideo(); // }// return null;// }
}
工厂模式
定义:定义一个创建对象的接口,但让实现这个接口的类来决定实例化那个类,工厂方法让类的实例化推迟到子类中进行。
适用场景: 创建对象需要大量重复的代码
客户端(应用层)不依赖于产品类实例如何被创建 实现等细节
一个类通过其子类来创建那个对象
优点:用户只需要关心所需产品对应的工厂,无须关心创建细节
加入新产品符合开闭原则,提高可扩展性
缺点: 类的个数容易过多,增加复杂度
增加了系统的抽象性和理解难度
抽象工厂模式
定义:抽象工厂模式提供一个创建一些列相关或相互依赖对象的接口
无须指定它们具体的类
适用场景:客户端(应用层)不依赖于产品类实例如何被创建 实现等细节
强调一些列相关的产品对象(属于同一产品族)一起适用创建对象需要大量重复的代码
提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
优点: 具体产品在应用层代码隔离,无须关心创建细节
将一个系列的产品族统一到一起创建
缺点:规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口。
增加了系统的抽象性和理解难度
建造者模式
定义: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
用户只需指定需要建造的类型就可以得到它们,建造过程及细节不需要知道
适用场景: 如果一个对象有非常复杂的内部结构(很多属性)
想把复杂对象的创建和使用分离
优点: 封装性好,创建和使用分离
扩展性好 建造类之间独立,一定程度上解耦
缺点:
产生多余的Builder对象
产品内部发生变化,建造者都要修改,成本较大
package com.keyi.design.parrttern.builder.v2;public class Course {private String CourseName; private String CoursePPT;private String CourseVideo;private String CourseArticle;public Course(CourseBuilder courseBuilder) {this.CourseName = courseBuilder.courseName;this.CoursePPT = courseBuilder.coursePPT;this.CourseVideo = courseBuilder.courseVideo;this.CourseArticle = courseBuilder.courseArticle;}@Overridepublic String toString() {return "Course{" +"CourseName='" + CourseName + '\'' +", CoursePPT='" + CoursePPT + '\'' +", CourseVideo='" + CourseVideo + '\'' +", CourseArticle='" + CourseArticle + '\'' +'}';}public static class CourseBuilder {private String courseName;private String coursePPT;private String courseVideo;private String courseArticle;public CourseBuilder builderCourseName(String CourseName) {this.courseName = CourseName;return this;}public CourseBuilder builderCoursePPT(String coursePPT) {this.coursePPT = coursePPT;return this;}public CourseBuilder builderCourseVideo(String courseVideo) {this.courseVideo = courseVideo;return this;}public CourseBuilder builderCourseArticle(String courseArticle) {this.courseArticle = courseArticle;return this;}public Course build() {return new Course(this);}}
}
单例模式
保证一个类仅有一个实例,并提供一个全局访问点
适用场景:想确保任何情况下都绝对只有一个实例
优点: 在内存中只有一个实例,减少了内存开销
可以避免对资源的多重占用
设置全局访问点,严格控制访问
缺点: 没有接口,扩展困难
重点: 私有构造器 线程安全 延迟加载 序列化和反序列化安全 反射
单例模式和工厂模式
单例模式和享元模式
懒汉式
特点: 线程安全 内存消耗低 资源开销大 性能有影响
public class LazySingleton {private static LazySingleton lazySingleton=null;private LazySingleton() {}public synchronized static LazySingleton getInstance(){if(lazySingleton==null){lazySingleton=new LazySingleton();}return lazySingleton;}
}
Double Check 双重检查锁的机制
public class LazySingletonDoubleCheck {//volatile 防止多线程中指令重排序 CPU共享内存 可见性private volatile static LazySingletonDoubleCheck lazySingletonDoubleCheck=null;private LazySingletonDoubleCheck() {}public synchronized static LazySingletonDoubleCheck getInstance(){if(lazySingletonDoubleCheck==null){synchronized (LazySingletonDoubleCheck.class){if(lazySingletonDoubleCheck==null){lazySingletonDoubleCheck=new LazySingletonDoubleCheck();//1.分配内存//2.初始化对象//3.设置LazySingletonDoubleCheck 指向刚分配的内存地址}}}return lazySingletonDoubleCheck;}
}
存在问题: 容易被反射破坏(反射可以修改构造器的权限)
public class StaticInnerClassSingleton {public static class InnerClass {private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();}private StaticInnerClassSingleton() {if (InnerClass.staticInnerClassSingleton != null) {throw new RuntimeException("单例模式禁止被初始化");}}public static StaticInnerClassSingleton getInstance() {return InnerClass.staticInnerClassSingleton;}
}
饿汉式
优点:类加载的时候就开始初始化 避免了线程同步的问题
缺点: 类加载的时候就开始初始化 没有延迟加载的效果 从来没有过 导致内存开销浪费
/*** 饿汉式 类加载的时候就开始了初始化*/
public class HungrySingLeton {private final static HungrySingLeton hungrySingLeton = new HungrySingLeton();private HungrySingLeton() {if (hungrySingLeton!=null){throw new RuntimeException("单例模式禁止被初始化");}}public static HungrySingLeton getInstance(){return hungrySingLeton;}/*** 防止反射获取对象* @return*/public Object readResolve() {return hungrySingLeton;
}
}return hungrySingLeton;}
}
可以防止反射攻击(因为初始化的工作在类加载的时候就已经初始化好了,可以通过私有化构造器判断这个对象是否初始化的状态 来判定对象是否初始化)
枚举单例模式
public enum EnumInstance {INSTANCE;private Object data;public Object getData() {return data;}public void setData(Object data) {this.data = data;}public static EnumInstance getInstance(){return INSTANCE;}
}
解决了反射和序列化和反序列化的问题 (反编译看枚举的构造方法是有参和私有的) 比较像饿汉式 (反编译看初始化操作是静态代码块执行的)
容器单例模式
/*** 容器单例模式*/
public class ContainerSingleton {private static Map<String, Object> singletonMap = new HashMap<>();private ContainerSingleton() {}public static void putInstance(String key, Object instance) {if (StringUtils.isNotBlank(key) && instance != null) {singletonMap.put(key, instance);}}public static Object getInstance(String key) { return singletonMap.get(key);}
}
原型模式
定义: 指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
不需要指定任何创建的细节,不调用构造函数
适用场景
类初始化消耗较多资源
new产生的一个对象需要非常繁琐的过程(数据准备 访问权限等)
构造函数比较复杂
循环体中生产大量对象时
优点: 原型模式性能比直接new一个对象性能高
简化创建过程
缺点: 必须配备克隆方法
对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入风险
深拷贝 浅拷贝要运用得当
package com.keyi.design.parrttern.prototype;
//浅克隆
public class Mail implements Cloneable {private String name;private String subject;private String body;public Mail() {System.out.println("Mail object created");}public String getSubject() {return subject;}public void setSubject(String subject) {this.subject = subject;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getBody() {return body;}public void setBody(String body) {this.body = body;}@Overridepublic String toString() {return "Mail{" +"name='" + name + '\'' +", subject='" + subject + '\'' +", body='" + body + '\'' +'}';}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
public class A implements Cloneable{@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
public class B extends A{public static void main(String[] args) throws CloneNotSupportedException {B b = new B();B clone = (B) b.clone();}
}
public class Pig implements Cloneable{public String name;private Date age;public Pig(String name, Date age) {this.name = name;this.age = age;}public Date getAge() {return age;}public void setAge(Date age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Pig{" +"name='" + name + '\'' +", age=" + age +'}'+super.toString();}//深克隆@Overrideprotected Object clone() throws CloneNotSupportedException {Pig pig = (Pig) super.clone();pig.age = (Date) age.clone();return pig;}}
结构型
外观模式
定义: 又叫门面模式,提供了一个统一的接口,用来访问子系统中的一群接口
外观模式定义了一个高层接口,让子系统更容易使用
适用场景: 子系统越来越复杂,增加外观模式提供简单调用接口
构建多层系统结构,利用外观对象作为每层的入口,简化层间调用
优点: 简化了调用过程,无需了解深入子系统,防止带来风险。
减少系统依赖 松散耦合
更好的划分访问层次
符合迪米特法则,即最少知道原则
缺点: 增加子系统,扩展子系统行为容易引入风险
不符合开闭原则
外观模式和中介者模式
外观模式和单例模式
外观模式和抽象工作模式
装饰着模式
定义:在不改变原有对象的基础上,将功能附加到对象上
提供了比继承更有弹性的替代方案(扩展原有对象功能)
适用场景: 扩展一个类的功能或给一个类添加附加职责
动态的给一个对象添加功能,这些功能可以再动态的撤销
优点: 继承的有力补充,比继承灵活,不改变原有对象的情况下给一个对象扩展功能
通过使用不同装饰类以及这些 装饰类的排列组合,可以实现不同的效果。
符合开闭原则
缺点: 会出现更多的代码,更多的类,增加程序复杂性
动态装饰时,多层装饰时会更复杂
装饰着模式和代理模式
装饰者模式和适配器模式
适配器模式
定义: 将一个类的接口转换成客户期望的另一个接口
使原本接口不兼容的类可以一起工作
应用场景: 已经存在的类,它的方法和需求不匹配的(方法结果相同或相似)
不是软件设计阶段考虑的模式,是随着软件维护,由于不同产品,不同厂家造成功能类似而接口不同情况下的解决方案。
优点: 能提高类的透明性和复用,现有的类复用但不需要改变
目标类和适配器类解耦,提高程序扩展性
符合开闭原则
缺点: 适配器编写过程需要全面考虑,可能回增加系统的复杂性
增加系统代码可读的难度
扩展: 对象适配器 类适配器
适配器模式和外观模式
类适配器
public interface Target {void request();
}
public class ConcreteTarget implements Target{@Overridepublic void request() {System.out.println("ConcreteTarget request");}
}
/*** 适配器模式*/
public class Adapter extends Adaptee implements Target {@Overridepublic void request() {super.adapteeRequest();}
}
public class Adaptee {public void adapteeRequest(){System.out.println("被适配者的方法");}
}
对象适配器
/*** 适配器对象模式*/
public class Adapter implements Target{private Adaptee adaptee=new Adaptee();@Overridepublic void request() {adaptee.adapteeRequest();}
}
享元模式
定义: 提供了减少对象数量从而改善应用所需的对象结构的方式
运用共享技术有效地支持大量细粒度的对象
使用场景: 常常应用于系统底层的开发,以便解决系统的性能问题。
系统有大量相似对象 需要缓冲池的场景
优点: 减少对象的创建,降低内存中对象的数量,降低系统的内存,提高效率
减少内存之外的其他资源占用
缺点: 内/外部状态 关注线程安全问题
使系统 程序的逻辑复杂化
扩展: 内部状态 外部状态
享元模式和代理模式
享元模式和单例模式
public interface Employee {void report();
}
public class EmployeeFactory {private static final Map<String, Employee> EMPLOYEE_MAP = new HashMap<String, Employee>();private EmployeeFactory() {}public static Employee getManger(String department) {Manger manger = (Manger) EMPLOYEE_MAP.get(department);if (manger == null) {manger = new Manger(department);System.out.println("创建部门经理:" + department);manger.setReportContent(department + "部门汇报:......");EMPLOYEE_MAP.put(department, manger);}return manger;}}
public class Manger implements Employee{@Overridepublic void report() {System.out.println(reportContent);}private String department;private String reportContent;public Manger(String department) {this.department = department;}public void setReportContent(String reportContent) {this.reportContent = reportContent;}
}
组合模式
定义:将对象组合成树形结构以表示“部分-整体”的层次结构
组合模式使客户端对单个对象和组合对象保持一致的方式处理
适用场景: 希望客户端可以忽略组合对象与单个对象的差异时
处理一个树形结构时
优点: 清楚地定义分层次的复杂对象,表示对象的全部或部分层次
让客户端忽略了层次的差异,方便对整个层次结构进行控制
简化客户端代码
符合开闭原则
缺点: 限制类型时会较为复杂
使设计变得更加抽象
组合模式和访问者模式
桥接模式
定义: 将抽象部分与它的具体实现部分分离,使它们都可以独立地变化
通过组合的方式建立两个类之间联系,而不是继承
适用场景: 抽象和具体实现之间增加更多的灵活性
一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展。
不希望适用继承,或因为多层继承导致类的个数剧增
优点:分离抽象部分及其具体实现部分
提高了系统的可扩展性
符合开闭原则
缺点: 增加了系统的理解与设计难度
需要正确地识别出系统中两个独立变化的维度
桥接模式和组合模式
桥接模式和适配器模式
代理模式
定义: 为其他对象提供一种代理,以控制对这个对象的访问
代理对象在客户端和目标对象之间起到中介的作用
优点: 代理模式能将代理对象与真实被调用的目标对象分离
一定程序上降低了系统的耦合度,扩展性好
保护目标对象
增强目标对象
缺点:代理模式会造成系统设计中类的数目增加
在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢
增加系统的复杂度
代理模式和装饰着模式
扩展:
静态代理:
动态代理:
CGLib代理: