【类(接口)之间的关系】
1. 继承
指的是一个类(一个接口)继承自另一个类(接口)的功能
既可以保留父类(父接口)的原有功能,又可以拓展自己新的功能
继承是类和类之间最常见的关系
在java中通过关键字 extends 明确标识,在设计时一般没有争议性
class A extends (class)B
interface A extends (interface)B
* 注意: 继承表示子类是一种特殊的父类,也就是 is-a 的关系
2. 实现
指的是一个类实现一个(或多个)接口,
实现是类和接口之间最常见的关系
在java中通过关键字 implements 明确标识, 在设计时也没有争议性
class A implements (interface)B
3. 依赖
指的是一个类A使用到了另一个类B
这种关系具有偶然性、临时性、非常弱的
但是B类的改变会影响到A
举例:人要过河,要借助船
在代码层面,类B作为参数在A的某个方法中使用
class A{
B - (抽象类)船
public void a(B b){
}
}
4. 关联
指的是两个类,或者类和接口之间语义级别的一种强依赖关系
举例:我和我朋友
体现在代码层面,类B作为一个成员属性(全局变量)在类A中出现
class A{
B b;
}
5. 聚合
是一种关联关系的特例,表示部分与整体,一种拥有的关系,
即 has-a 的关系, 此时整体和部分是可以分离的
举例:公司与员工
体现在代码层面,只能通过语义区分
6. 组合
也是关联关系的一种特例,这种关系比聚合更强,也称为强聚合
此时部分和整体是不可分离的,
部分的生命周期结束意味着整体的生命周期也结束了
举例:我和我的大脑
体现在代码层面,只能通过语义区分
总结:
关系强弱程度分为别: 组合 > 聚合 > 关联 > 依赖
雁群 大雁 -> 聚合
大雁 翅膀 -> 组合
在书写代码时,要注意
1) 雁群和大雁是聚合关系,意味着大雁是可以脱离雁群而独立存在的
在构造对象时,可以把大雁作为一个参数传入雁群的构造器
2) 大雁和翅膀是组合关系,意味着翅膀是不能脱离大雁而独立存在的
在设计时,往往会把翅膀设计成大雁的内部类,在大雁的构造器中构造翅膀对象
翅膀对象也不能给外界提供访问的功能
【六大设计原则】
1. 单一职责原则
不要存在多于一个导致类改变的原因
简单的说,一个类就负责一项职责
* 没有说一个类只能有一个方法
优点:
1) 降低类的复杂度,一个类只负责一项职责,其逻辑肯定比负责多项职责简单的多
2) 提高类的可读性,提高系统的可维护性
3) 变更引起的风险降低,这个原则遵循的好,可以显著降低对其他功能的影响
4) 需要说明的是,单一职责原则不是OOP程序所独有的,只要是模块化的程序设计,都必须遵守这个原则
2. 里氏替换原则
在任何基类可以出现的地方,子类必定可以出现
3. 依赖倒转原则
高层模块不应该依赖于底层模块,二者都应该依赖其抽象
抽象不应该依赖于细节,细节应该依赖于抽象
类A直接依赖于类B,假如未来有可能改成类A依赖于类C,这种场景下
类A就是高层模块,负责复杂的业务逻辑;
类B和类C是低层模块,负责基本的原子操作
* 面向对象 -> 面向接口
4.接口隔离原则(Interface Segregation Principle, ISP)
使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口
使用多个隔离的接口,比单个接口要好
不要强迫一个类去实现它没有必要实现的功能
5. 迪米特法则(最少知道原则)
一个对象应该对其他对象保持最少的了解
类和类之间的关系越紧密,耦合度越大,当一个类发生改变时,对另一个类影响也越大
迪米特法则还有另一个定义: 只与直接的朋友通信
6. 开闭原则(最重要)
一个软件实体类、模块、方法应该对扩展开放,对修改关闭。
【设计模式】
1. 是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结
2. 使用设计模式的作用
1) 最佳的实践
设计模式已经经历了相当长的一段时间的发展
它提供了软件开发中面临一般问题的最佳解决方案
适合经验不足的开发人员通过一种快捷的方式设计出优秀的程序
2) 开发人员的共同平台
【单例模式】
设计了一种只有一个对象的类,这种类自己负责创建自己的对象。
同时确保自己只有一个对象被创建。
这个对象同时也可以在程序的各个地方被访问
1. 懒汉模式:
第一次访问时,才会创建对象
2. 饿汉模式:
加载类的时候就创建对象
3.防止并发的单例模式
4.防止反射的单例模式
6.枚举类型的单例模式
【工厂模式】
在工厂模式中, 我们在创建对象时不会对用户暴露创建逻辑
并且是通过一个共同的接口来指向新创建的对象
优点:
1. 调用者想创建一个对象, 只需要知道名字就可以了(直接调用工厂模式的静态方法,传入类名)
2. 拓展性高, 如果想增加一种产品, 只要拓展一个工厂类就可以
3. 屏蔽了产品的具体实现, 用户只关心产品的接口
缺点:
每次增加一种产品, 必然要增加一种工厂. 会导致系统中的类越来越多,
提高系统的复杂性, 有可能不是一件好事
【建造者模式】
Builder模式
使用前:
Student s2 = new Student(2, "李四");
s2.setAge(21);
s2.setHobby("写代码");
使用后
Student s2 = new Student.StudentBuilder(2, "李四").age(21).hobby("写代码").build();
package com.bwf.builder;
public class Student {
// 必选项
private int id;
private String name;
// 可选项
private int age;
private boolean married;
private String hobby;
public Student(int id, String name) {
super();
this.id = id;
this.name = name;
}
public Student(StudentBuilder sb) {
this.id = sb.id;
this.name = sb.name;
this.age = sb.age;
this.married = sb.married;
this.hobby = sb.hobby;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isMarried() {
return married;
}
public void setMarried(boolean married) {
this.married = married;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", age=" + age + ", married=" + married + ", hobby=" + hobby
+ "]";
}
public static class StudentBuilder{
// 必选项
private int id;
private String name;
// 可选项
private int age;
private boolean married;
private String hobby;
// 构造器里放必选项
public StudentBuilder(int id, String name) {
super();
this.id = id;
this.name = name;
}
// 可选项操作
public StudentBuilder age(int age) {
this.age = age;
return this;
}
public StudentBuilder married(boolean married) {
this.married = married;
return this;
}
public StudentBuilder hobby(String hobby) {
this.hobby = hobby;
return this;
}
public Student build() {
return new Student(this);
}
}
}
【装饰模式】
1. 案例
一点点订单系统
红茶
绿茶
乌龙茶
奶茶 牛奶 + 红茶
波霸奶茶 珍珠 + 牛奶 + 红茶
奶绿 牛奶 + 绿茶
乌龙玛奇朵 乌龙茶 + 奶霜
椰果奶茶 椰果 + 牛奶 + 红茶
冰淇淋红茶
红茶玛奇朵
功能: 1. 描述 2. 价格
2. 装饰模式允许向一个现有的对象添加新的功能
同时又不改变其结构
这种类型的设计模式属性结构型模式, 它是作为一种对现有类的一个包装
这种模式创建了一个装饰类用来包装原有的类
并在保持类方法签名完整性的前提下, 提供了额外的功能
【代理模式】
概念参考自此文章
https://www.cnblogs.com/chentingk/p/6433372.html
代理模式这种设计模式是一种使用代理对象来执行目标对象的方法并在代理对象中增强目标对象方法的一种设计模式。代理对象代为执行目标对象的方法,并在此基础上进行相应的扩展。
代理模式分为静态代理,动态代理(jdk动态代理、cglib动态代理、Spring和AspectJ实现的动态代理)
这里只讲静态代理和jdk动态代理,后面3个在06-2spring框架中再写
https://blog.csdn.net/qq_36194262/article/details/83783938
【静态代理】
首先有如下需求,
写一个用户工具类接口和它的实现类,有增删改查的方法
package com.bwf.proxy;
public interface UserUtil {
void insert();
void delete();
void update();
void query();
}
package com.bwf.proxy;
public class UserUtilImpl1 implements UserUtil{
@Override
public void insert() {
System.out.println("增");
}
@Override
public void delete() {
System.out.println("删");
}
@Override
public void update() {
System.out.println("改");
}
@Override
public void query() {
System.out.println("查");
}
}
现在又有新的需求加入,要求在增删改查之前增加用户验证,那么修改成如下的代码
package com.bwf.proxy;
public class UserUtilImpl2 implements UserUtil{
@Override
public void insert() {
System.out.println("权限验证");
System.out.println("增");
}
@Override
public void delete() {
System.out.println("权限验证");
System.out.println("删");
}
@Override
public void update() {
System.out.println("权限验证");
System.out.println("改");
}
@Override
public void query() {
System.out.println("权限验证");
System.out.println("查");
}
}
可以看到,虽然成功实现了这一功能,但是重复代码很多,每个方法内都有权限检查模块.而且维护起来也很麻烦,需要修改每一个方法.
静态代理就是写死了在代理对象中执行这个方法前后执行添加功能的形式,每次要在接口中添加一个新方法,则需要在目标对象中实现这个方法,并且在代理对象中实现相应的代理方法,幸而Java有独特的反射技术,可以实现动态代理。
【动态代理】
java反射包中提供了Proxy类和InvocationHandler接口来实现动态代理
Proxy类
用来生成代理对象的一个类,该类中的newProxyInstance方法用于创建一个对象的代理对象
查看jdk api:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
/*方法解释:返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
方法参数:
loader - 定义代理类的类加载器
interfaces - 代理类要实现的接口列表
h - 指派方法调用的调用处理程序*/
该方法中的第三个参数是一个InvocationHandler接收的实例,用来写具体增强的方法
InvocationHandler接口
该接口只有一个invoke方法,实现接口后重写即可
//invoke方法
Object invoke(Object proxy,
Method method,
Object[] args)
throws Throwable
//方法解释:在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
//方法参数:proxy - 在其上调用方法的代理实例
// method - 对应于在代理实例上调用的接口方法的 Method 实例。 Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
// args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基 本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。
代码实现:
写一个类实现InvocationHandler接口,在该类的构造器里传入被强化的对象,写一个getProxy方法(获得强化对象),方法用户实例化被强化对象,最后重写invoke方法,用来添加具体强化的内容
package com.wowowo.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.wowowo.service.UserService;
public class UserUtilProxy implements InvocationHandler {
/*本类用来强化UserUtilImpl方法,主方法通过构造本类对象,
传入原本的对象,返回的是增强过的对象*/
private UserUtil uu;
//传入原本的对象,准备强化
public UserUtilProxy(UserUtil uu) {
super();
this.uu = uu;
}
/**
* 获得uu的代理对象 具有事务的功能
* @return
*/
public UserUtil getProxy(){
/*newProxyInstance这个方法有三个参数,第一个是被代理对象的类加载器,第二个是该类的接口,所以说被代理的对象必须要实现接口,第三个参数是InvocationHandler 接口的实现类
这里很巧妙,通过当前类实现InvocationHandler接口,重写invoke方法(写你要增强的代码),这时候第三个参数就可以传this(当前对象,因为我实现了InvocationHandler接口)*/
Object proxy = Proxy.newProxyInstance(uu.getClass().getClassLoader()
, uu.getClass().getInterfaces(), this);
return (UserUtil) proxy;
}
/*
* 在invoke方法编码指定返回的代理对象干的工作
* proxy : 代理对象
* method: 代理方法调用的对象
* args: 方法参数
*
* 当调用代理对象执行方法时,
* 实际上执行的都是invoke方法里面的代码,
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("打开事务");
Object res = method.invoke(uu, args);
System.out.println("提交事务");
return res;
}
}
构造本类传入被强化的对象并调用getProxy方法即可获得被强化的对象.