UML类图、JAVA DesignMode 设计模式、设计原则

全文2.6W余字,读完需要20分钟,介绍23种设计模式,每个模式都有案例与真实使用场景,能够帮助初学者快速了解设计模式,建立起对代码中设计模式的初步理解,要明确:设计模式只是帮助我们更好的设计代码架构,低耦合高内聚是我们不变的目标。

参考文献:《Head First 设计模式》【美国】弗里曼

JAVA DesignMode

在学习设计模式之前,建议首先学习UML类图。

统一建模语言(Unided Modeling Language)是用来设计软件的可视化建模语言,UML从目标系统的不同角度出发,定义了用例图、类图、对象图、状态图、活动图、时序图、协作图、部署图等9种图。

一,UML类图

  类图(Class diagram)是显示了模型的静态结构,特别是模型中存在的类、类的内部结构以及它们与其它类的关系等。

1.1 类图的表现方式

  在UML类图中,类使用包含类名、属性(field)和方法(method)且带有分割线的矩阵来表示,比如下图就表示一个Employee类:

1.2 类与类之间的关系表示方式

  关联关系时对象之间的一种引用关系,用于表示一类对象与另一类对象之间的联系,如老师和学生、上级与下级等。关联关系时类与类之间最常用的一种关系,分为一般关联关系、聚合关联关系和组合关系。

  1. (一般)关联关系

    关联关系又可以分为单向关联、双向关联、自关联。

    1. 单向关联

      在UML类图中,单向关联用一个带三角箭头的实线表示。上图中每个顾客都有一个地址,这通过让Customer类持有一个类型为Address的成员变量类实现。

    2. 双向关联

      所谓双向关联,就是双方各自持有对方类型的成员变量,在UML类图中,双向关联用一个不带箭头的实线表示。上图中在Customer类中维护了一个List,表示一个顾客可以购买多个商品;在Product类中维护一个Customer类型的成员变量则表示这个产品被哪个顾客所购买。

    3. 自关联

      自关联在UML类图中,用一个带三角箭头且指向自身的线表示。上图表示Node类包含类型为Node的成员变量,也就是自己包含自己。

  2. 聚合关系

      聚合关系是关联关系中的一种,是强关联关系,表示整体与部分之间的关系。

      其也是通过成员对象来实现的,其中成员对象是整体对象的一部分,但是成员对象可以脱离整体对象而独立存在。例如学校与老师的关系,学校是整体包含老师这个成员对象,如果学校休学了那老师依然存在。在UML类图中,聚合关系可以用空心的菱形实现来表示,菱形指向整体部分

  3. 组合关系

      组合关系表示类之间整体与部分的关系,但它是一种更强烈的聚合关系

      在组合关系中,整体对象可以控制部分对象的生命周期,一旦整体对象不存在了,那么部分对象也将不复存在,部分对象不能脱离整体对象而存在,例如头和嘴巴的关系,没有了头嘴巴也就不存在了。在UML类图中,组合关系使用带实心菱形的实线来表示,实心菱形指向整体部分

  4. 依赖关系

      依赖关系时一种使用关系,它是对象之间耦合度最弱的一种关系,是临时性的关联。在代码中,某个类的方法通过局部变量、方法的参数或者对静态方法的调用来访问另一个类(被依赖的类)中的某些方法来完成一些职责。

      在UML类图中,依赖关系使用带箭头的虚线来表示,箭头从使用类指向被依赖的类。下图所示司机与汽车的关系图,司机驾驶汽车:

  5. 继承关系

      继承关系是对象之间耦合度最大的一种关系,表示一般与特殊的关系,是父类与子类之间的关系,是一种继承关系。

      在UML类图中,泛化(继承)关系用带空心的三角箭头的实线来表示,箭头从子类指向父类。例如Student类和Teacher类都是Person类的子类:

  6. 实现关系

      实现关系时接口与实现类的关系,在这种关系中,类实现了目标接口,类中的操作实现了接口中所声明的所有抽象操作。

      在UML类图中,实现关系使用带空心三角箭头的虚线来表示,箭头从实现类指向接口。例如汽车和船都实现了交通工具接口:

二,设计原则

模式:就是在某种情景下,针对某问题的某种解决方案

  • 抽象原则:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
  • 组合原则:多用组合,少用继承(Java中是单继承)。
  • 面向接口编程原则:针对接口编程,而不是针对实现编程(Spring中的ApplicationContext高级展现形式就体现了该原则)。
  • 松耦合编程原则:为了交互对象之间的松耦合设计而努力。
  • 开闭原则:类应该对扩展开放,对修改关闭。
  • 依赖倒置原则:依赖抽象,不要依赖具体类。
  • 最少知识原则:只和你的密友谈话。
  • 好莱坞原则:别调用(打电话)我们,我们会调用(打电话)给你。
  • 单一责任原则:一个类应该只有一个引起变化的原因(高内聚:用来度量一个类或模块紧密地达到单一目的或责任)。类的每个责任都有改变的潜在区域,超过一个责任,则意味着超过一个改变的区域。当一个模块或一个类被设计成只支持一组相关的功能时,我们称之为该类是高内聚的。

三,创建型模式:

image-20230212141107757

1)工厂方法模式

  工厂方法模式是简单工厂模式的进一步抽象和推广,它让类的实例化推迟到子类中进行。工厂方法模式定义了一个创建对象的接口,但是要由子类来决定实例化的类是哪一个,把创建对象的操作下发到对象工厂中去完成。

  工厂方法模式常用于解决对象多态的问题,对应对象的创建应当把创建方法与实例化方法解耦,类似现在要做一个披萨,但是这个披萨有很多种类型,在实例化时想根据用户的需求动态的创建披萨,这时候就可以用到工厂方法模式,让工厂来帮助我们创建对象。

注意:正如前面所说,工厂方法让子类来决定要实例化的类是哪一个,这里所谓的“决定”,并不是指模式运行子类本身在运行时做决定,而是指在编写创建类时,不需要知道实际要创建的产品是哪一个,选择了使用哪个子类自然就决定了实际创建的产品是什么。

工厂方法模式类图:

image-20230212131201607

  在Spring中Bean的创建就使用了工厂方法模式,FactoryBean是Spring中的一个接口,常用来获取单例Bean实例。当一个类实现了FactoryBean接口,那么当我们从Spring IOC容器中获取该类的实例时,Spring会调用getObject()方法(如果子类重写了该方法),把方法的返回结果给我们。

// 定义一个Bean, 实现FactoryBean(工厂模式)接口
@Component
public class MyFactoryBean implements FactoryBean<MyFactoryBean> {
   
	private String name;

	@Override
	public MyFactoryBean getObject() {
   
		MyFactoryBean myFactoryBean = new MyFactoryBean();
		myFactoryBean.setName("通过getObject方法初始化实例=========");
		System.out.println("(1) MyFactoryBean.getObject()");
		return myFactoryBean;
	}

	@Override
	public Class<?> getObjectType() {
   
		System.out.println("(2) MyFactoryBean.getObjectType()");
		return MyFactoryBean.class;
	}

	// 省略构造函数、get、set、toString方法
}

  我们先看容器启动之后,三个bean的情况,打上断点查看((AnnotationConfigApplicationContext) context).beanFactory.singletonObjects.get("myFactoryBean")

可以清楚的看到此时的myFactoryBean实例是Spring通过无参构造函数来创建的,name还是null值:

image-20230205114459010

当程序执行context.getBean(MyFactoryBean.class)代码时:

  1. Spring准备从ioc容器中获取该bean;
  2. 发现该bean实现了FactoryBean接口,说明这是一个工厂bean,那么就不从ioc容器中获取该bean的实例了,去调用FactoryBean.getObject()方法获取其bean的实例;
  3. ioc容器虽然已经为该bean做了实例化的操作,但是由于你实现了FactoryBean接口,你是一个工厂Bean,所以你的实例还是自己去创建吧!将Bean的实例化推迟到子类中进行

image-20230205114834304

  (Spring)工厂方法模式定义了一个创建对象的接口(FactoryBean),但是要由子类(MyFactoryBean)来决定实例化的类是哪一个,把创建对象的操作下发到对象工厂(实现了FactoryBean接口的类)中去完成。

2)抽象工厂模式

  抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

  抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道(或关心)实际产出的具体产品是什么,这样一来,客户就可以从他们的具体产品中解耦。抽象工厂中的每个方法实际上都是工厂方法。抽象工厂的任务是定义一个负责创建一组产品的接口,这个接口内的每个方法都负责创建一个具体的产品,同时我们利用实现抽象工厂的子类来提供这些具体的做法。

抽象工厂与工厂方法的区别:

  • 整个工厂方法模式,不过是通过子类来创建对象(比如创建一个披萨),用这种方法,客户只需要知道他们所使用的抽象类型就可以了,而由子类来负责决定具体的类型。所以工厂方法模式只负责将客户从具体类型中解耦;
  • 抽象工厂模式提供了一个用来创建一个产品家族的抽象类型(比如创建一个披萨,但是还要包含披萨的原材料),这个类型的子类定义了产品被产生的方法;
  • 工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个;
  • 工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。

抽象工厂模式类图:

image-20230212145605887

  Spring中的BeanFactory就是采用了抽象工厂模式的设计思路,当你的bean实现BeanFactory接口时,实例化bean会推迟到具体子类中进行,而在Spring的内部,BeanFactory接口又被其他抽象工厂互相实现,Spring org.springframework.beans.factory.BeanFactory源码:

/**
 *    BeanFactory 接口中的方法, 其中都是有关于Bean的操作, 要么通过name, 类型的字节码去获取Bean, 要么就是根据名称或者ResolvableType来判断
 * Bean是singleton还是prototype的, 但是这里面没有关于Bean注入的内容, Spring底层提供了Bean注册中心, 而BeanFactory接口主要是声明获取Bean信息的相关方法.
 */
public interface BeanFactory {
   
	// 1. beanFactory 自带的bean 前缀
	String FACTORY_BEAN_PREFIX = "&";
	// 2. 根据bean的name获取Bean, Bean的默认name为首字母小写的类名
	Object getBean(String name) throws BeansException;
	// 3. 根据bean的name获取Bean, 并转化为指定的对象
	<T> T getBean(String name, Class<T> requiredType) throws BeansException;
	// 4. 根据bean的name获取Bean, 并传入构造该bean的参数, 用于有参构造
	Object getBean(String name, Object... args) throws BeansException;
	// 5. 根据字节码获取bean
	<T> T getBean(Class<T> requiredType) throws BeansException;
	// 6. 根据字节码获取bean, 并传入构造该bean的参数, 用于有参构造
	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
	// 7. 根据字节码获取指定bean 的提供者
	<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
	// 8. 根据指定的解析类型获取bean 的提供者
	<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
	// 9. 判断工厂中是否包含指定bean
	boolean containsBean(String name);
	// 10. 根据beanName来判断该bean是否是单例的
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
	// 11. 根据beanName来判断该bean是否是原型对象
	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
	// 12. 根据名称判断Bean 是否能被指定的可解析类型解析
	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
	// 13. 根据名称判断Bean 是否被指定的字节码对应的类解析
	boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
	// 14. 根据名称获取bean的类型对应的字节码
	@Nullable
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;
	// 15. 根据名称获取Bean 的类型对应的字节码, 并设置是否允许Bean初始化
	@Nullable
	Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
	// 16. 获取Bean 的别名
	String[] getAliases(String name);
}

  在Spring中,自带的BeanFactory接口实现类就有大几十个,BeanFactory顶层接口中声明了关于获取Bean信息的方法,其他所有的工厂都是BeanFactory的子类,虽然在BeanFactory中并没有显示的声明具体有哪些工厂,但这些工厂都会来实现BeanFactory接口。这就是抽象工厂模式的设计方法,BeanFactory就相当于是抽象工厂,而其继承子类就相当于产品,不同的工厂类就相当于不同的产品。抽象工厂模式提供一个接口(BeanFactory),用于创建相关或依赖对象的家族,而不需要明确指定具体类。

image-20220801165329082

3)建造者模式

  建造者模式又称之为生成器模式,是将一个复杂对象的构建与它的表示进行分离,使得同样的构建过程可以创建不同的表示。用户只需要指定需要建造的类型就可以获得对象,建造过程及细节不需要了解。

建造者模式与工厂模式最大的区别在于,建造者模式更关注产品的组合方式与装配顺序,而工厂模式专注的是产品本身(换句话说:建造者模式更注重过程,而工厂模式只关注结果)。

建造者模式类图:

image-20230212131414616

JDK中的建造者模式:

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值