Java简单设计模式
V哥官网:http://www.vgxit.com
本博客对应视频教程:http://www.vgxit.com/course/21
1,为什么要学习这门课
我们很多人都喜欢金庸的武侠小说,他的小说里面也有过内功和招式谁更厉害的争论。像张无忌的九阳神功,乾坤大挪移等可以独步武林。而令狐冲的独孤九剑也是一败难求。对于学武功来说,当然,我们的内功和招式都很重要。但是老师觉得相比之下内功更加的重要。因为招式千篇一律,而内功才是练武的核心思维。
在我们的编程技术学习上也有内功和招式的区分。
2,Java反射技术
对于Java的反射技术,老师之前在讲Java基础入门的时候就讲过,这里我们就只是给大家做一下简单的复习。
Java反射技术应用广泛,它能够配置:类的全限定名、方法和参数,完成对象的初始化,甚至是反射某些方法。这样就可以大大增强 Java 的可配置’性, pring IoC 的基本原理也是如此,当然 Spring IoC 的代码要复杂得多。
(1),通过反射构建对象
假如我们有一个对象,名字叫做Person:
public class Person {
public void sayHello(String name) {
System.out.println("Hello " + name);
}
}
然后我们通过反射来调用Person的sayHello方法:
package com.vgxit.sjms.reflect.test;
import com.vgxit.sjms.reflect.Person;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class PersonTest {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException,
InstantiationException, NoSuchMethodException, InvocationTargetException {
//获取Person对应的Class
Class<Person> personClass = (Class<Person>) Class.forName("com.vgxit.sjms.reflect.Person");
//通过反射获取一个Person对象
Person person = personClass.newInstance();
//获取这个Person对象的sayHello方法
Method sayHelloMethod = personClass.getMethod("sayHello", String.class);
//调用sayHello方法
sayHelloMethod.invoke(person, "李一桐");
}
}
3,代理模式
代理模式非常重要,是我们SSM课程的超级重点内容。
那么什么是代理模式。这里我们给大家用一个现实中的例子举例。加入你们公司是一个接外包项目的软件公司,你是一位软件工程师。你们的客户要做一个项目,他会直接和你们做商务的同事谈,而不会和你谈。
下面我们就用一张图来表示代理模式的含义:
那有些同学可能就会有疑问了,说这个客户直接找软件工程师谈判不就好了吗?其实商务的作用也是很大的。比如项目启动前的商务谈判,软件价格,交付,进度的时间节点等,或者项目完成后的商务追讨应收账款等。商务也有可能在开发软件之前谈判失败,此时商务就会根据公司规则去结束和客户的合作关系,这些都不用软件工程师来处理。这里我们就可以看出来,商务控制了客户对软件工程师的方法。
经过上面的讲解,我们知道商务和软件工程师是代理和被代理的关系。客户是经过商务区访问软件工程师的。此时客户就是程序中的调用者,商务就是代理对象,软件工程师就是真实对象。我们需要在调用者和调用对象之间生成一个代理对象,而这个代理对象需要和真实对象建立代理关系,所以代理必须分为两个步骤:
- 代理对象和真实对象建立代理关系
- 实现代理对象和代理逻辑方法
4,动态代理分类
动态代理技术有两种。
- 一种是JDK动态代理,这是JDK自带的功能
- 另一种是CGLIB,这是第三方提供的一个技术
5,JDK动态代理
Jdk实现动态代理的基本步骤:
1,编写一个接口,这个接口对外提供需要被代理的方法
2,编写这个接口的实现类,这个实现类就是真实的要被代理的对象
3,编写一个InvocationHandler的实现类,它里面定义了一个invoke方法,这个方法就是我们调用代理对象的时候具体执行的方法。
对应代码如下:
1,首先我们编写对应的接口:
public interface HelloWorld {
void sayHelloWorld();
}
2,然后编写对应接口的实现类:
public class HelloWorldImpl implements HelloWorld {
@Override
public void sayHelloWorld() {
System.out.println("Hello world");
}
}
3,编写JdkProxyHandler的代理类
public class JdkProxyHandler implements InvocationHandler {
//真实对象
private Object target;
/**
* 建立代理对象和真实对象的代理关系,并返回代理对象
* @param target 真实对象
* @return 代理对象
*/
public Object bind(Object target) {
this.target = target;
/**
* 这个地方生成一个代理对象
* 代理对象的第一个参数就是一个具体的类加载器
* 第二个参数就是我们要代理的接口
* 第三个参数指定对应的invoke方法的持有类
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("进入代理逻辑方法...");
System.out.println("在调度真实对象之前的服务...");
Object obj = method.invoke(target, args);
System.out.println("在调度真实对象之后的服务...");
return obj;
}
}
4,具体调用:
public class JdkProxyHandlerTest {
public static void main(String[] args) {
JdkProxyHandler jdkProxyHandler = new JdkProxyHandler();
HelloWorld proxy = (HelloWorld) jdkProxyHandler.bind(new HelloWorldImpl());
proxy.sayHelloWorld();
}
}
理顺思路:
只有接口,没有实现类的动态代理(比如Mybatis整合spring的时候)
1,定义一个Annotation:
package com.vgxit.sjms.ktdm.ms;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Select {
String value();
}
2,定义一个Mapper接口:
package com.vgxit.sjms.ktdm.ms;
public interface UserMapper {
@Select("select * from user")
void all();
}
3,定义InvokeHandler:
package com.vgxit.sjms.ktdm.ms;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MybatisHandler implements InvocationHandler {
public Object createMapper(Class clz) {
Class[] interfaces = {clz};
return Proxy.newProxyInstance(this.getClass().getClassLoader(), interfaces, this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//1,获取对应方法上的注解
Select select = method.getAnnotation(Select.class);
//2,获取对应的查询语句
String sql = select.value();
//3,模拟查询sql语句
System.out.println("查询mysql的数据,对应的sql => " + sql);
System.out.println("查询完成");
return null;
}
}
4,具体调用
package com.vgxit.sjms.ktdm.ms;
public class MsTest {
public static void main(String[] args) {
MybatisHandler mybatisHandler = new MybatisHandler();
UserMapper userMapper = (UserMapper) mybatisHandler.createMapper(UserMapper.class);
userMapper.all();
}
}
4,具体的调用:
package com.vgxit.sjms.ktdm.cglib;
public class CglibTest {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
Person person = (Person) cglibProxy.getProxy(Person.class);
person.sayHello("李一桐");
}
}
6,CGLIB动态代理
JDK动态代理有一个非常大的缺陷,它必须要提供了接口才只能使用。但是,我们在开发的时候,很多时候是不方便提供接口的。比如我们有一个类,这个类是第三方的开源的jar包给我们提供的,他给我们提供的这个类并没有写接口。这个时候,我们就只能用其他的办法来解决了。这里老师给大家推荐一个开源工具叫做CGLIB。它可以是先动态代理,但是并不需要我们提供接口,只需要有一个非抽象的实现类就行了。
具体操作:
1,引入cglib:
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
2,定义一个具体的类(代理的真实类):
package com.vgxit.sjms.ktdm.cglib;
public class Person {
public void sayHello(String name) {
System.out.println("Hello " + name);
}
}
3,编写对应的代理管理类:
package com.vgxit.sjms.ktdm.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
/**
* 获取对应的代理类的方法
* @param cls
* @return
*/
public Object getProxy(Class cls) {
//获取CGLIB的enhancer增强对象
Enhancer enhancer = new Enhancer();
//设置增强的类型
enhancer.setSuperclass(cls);
//定义代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor
enhancer.setCallback(this);
//返回生成的代理对象,这个代理对象其实就是传入的class的对应类的子类
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("调用真实对象之前....");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("调用真实对象之后....");
return result;
}
}
7,拦截器
刚刚同学们跟老师一起了解了动态代理,我相信大家对动态代理应该有了一个初步的认识。
刚刚同学们跟老师一起了解了动态代理,我相信大家对动态代理应该有了一个初步的认识。但是动态代理的思想是非常复杂的,代码理解上来说不是太容易。所以很多框架一般给我们设计了拦截器来给开发者使用。开发者只需要知道拦截器接口的方法含义和作用就行了。比如我们Spring Mvc就为我们提供了拦截器。
这个时候对开发者来说,开发者只需要知道拦截器怎么写就好了,可以实现和动态代理一样的效果,而对于拦截器的实现原理我们可以不了解。
那么接下来我们就简单的做一个拦截器来实现:
1,定义接口(框架的设计者给我们做的):
package com.vgxit.sjms.ktdm.interceptor;
import java.lang.reflect.Method;
/**
* 自定义拦截器接口
*/
public interface Interceptor {
/**
* 拦截器的before方法,只有before方法返回true的时候才会真的去调用代理类的真实的方法
* @param proxy 代理对象
* @param target 目标对象
* @param method 对应的方法
* @param args 对应的参数
* @return
*/
boolean before(Object proxy, Object target, Method method, Object[] args);
/**
* 拦截器的around方法,如果before方法返回false,那么不会调用目标对象真实的方法,会调用这个方法
* @param proxy 代理对象
* @param target 目标对象
* @param method 对应的方法
* @param args 对应的参数
* @return
*/
void around(Object proxy, Object target, Method method, Object[] args);
/**
* 拦截器的after方法,最后调用的方法
* @param proxy 代理对象
* @param target 目标对象
* @param method 对应的方法
* @param args 对应的参数
* @return
*/
void after(Object proxy, Object target, Method method, Object[] args);
}
2,开发者自定义拦截器:
package com.vgxit.sjms.ktdm.interceptor;
import java.lang.reflect.Method;
public class VgeInterceptor implements Interceptor {
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("反射代理之前的代码...");
return true;
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
System.out.println("该方法取代了目标对象的真实方法...");
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("反射代理之后的方法");
}
}
3,定义拦截器代理(框架设计者提供的):
package com.vgxit.sjms.ktdm.interceptor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 拦截器代理
*/
public class InterceptorJdkProxy implements InvocationHandler {
//真实的目标对象
private Object target;
//定义拦截器的Class
private Class interceptorClass;
/**
* 绑定委托对象并返回一个代理
* @return
*/
public static Object bind(Object target, Class interceptorClass) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InterceptorJdkProxy(target, interceptorClass)
);
}
public InterceptorJdkProxy(Object target, Class interceptorClass) {
this.target = target;
this.interceptorClass = interceptorClass;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (interceptorClass == null) {
return method.invoke(target, args);
}
Object result = null;
//通过反射生成拦截器
Interceptor interceptor = (Interceptor) interceptorClass.newInstance();
if (interceptor.before(proxy, target, method, args)) {
result = method.invoke(target, args);
} else {
interceptor.around(proxy, target, method, args);
}
interceptor.around(proxy, target, method, args);
return result;
}
}
4,具体调用的方法:
package com.vgxit.sjms.ktdm.interceptor;
public class InterceptorTest {
public static void main(String[] args) {
Hello hello = (Hello) InterceptorJdkProxy.bind(new HelloImpl(), VgeInterceptor.class);
hello.sayHello("李一桐");
}
}
8,责任链模式
因为本课程的目的是设计模式的简单介绍,所以这个地方责任链模式的话,我们不会讲的太深入,之后我们专门的设计模式课程还会重新来讲解的。这里我们就是简单的提一下。
可能我们生活中存在多个拦截器的情况。比如我们的请假流程,如果请假的天数过多,那么需要先进过项目经理审批,然后再由部门经理审批,然后再由HR审批。如果我们把每一个请求的流程都看成是一个拦截器,那么就会出现我们一个流程多个拦截器的情况。
当一个对象在一条恋上被多个拦截器处理的时候,这种模式就叫做责任链模式。
案例:
1,定义三个拦截器:
package com.vgxit.sjms.ktdm.interceptor;
import java.lang.reflect.Method;
public class Interceptor1 implements Interceptor {
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("[拦截器1]的before方法");
return true;
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
System.out.println("[拦截器1]的around方法");
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("[拦截器1]的after方法");
}
}
package com.vgxit.sjms.ktdm.interceptor;
import java.lang.reflect.Method;
public class Interceptor2 implements Interceptor {
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("[拦截器2]的before方法");
return true;
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
System.out.println("[拦截器2]的around方法");
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("[拦截器2]的after方法");
}
}
package com.vgxit.sjms.ktdm.interceptor;
import java.lang.reflect.Method;
public class Interceptor3 implements Interceptor {
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("[拦截器3]的before方法");
return true;
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
System.out.println("[拦截器3]的around方法");
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("[拦截器3]的after方法");
}
}
具体的调用:
package com.vgxit.sjms.ktdm.interceptor;
public class InterceptorTest {
public static void main(String[] args) {
Hello proxy1 = (Hello) InterceptorJdkProxy.bind(new HelloImpl(), Interceptor1.class);
Hello proxy2 = (Hello) InterceptorJdkProxy.bind(proxy1, Interceptor2.class);
Hello proxy3 = (Hello) InterceptorJdkProxy.bind(proxy2, Interceptor3.class);
proxy3.sayHello("李一桐");
}
}
9,观察者模式:
观察者模式,又称为发布订阅模式,是对象的行为模式。观察者模式定义了一对多的依赖关系,让多个观察者对象同时监视者被观察对象,当被观察对象的状态发生改变了之后,会通知所有的观察者,并让其自动更新。
在现实中,有些条件发生了变化,其他行为也要发生变化。举个例子,我们的商家有一些产品,但是商家和电商平台和做,每当有产品更新的时候,就会把这些更新后的产品推送给对应的合作电商平台,比如有淘宝,天猫,京东。我们按照之前的方式用伪代码来表示一下:
if (产品更新了) {
推送到淘宝;
推送到京东;
}
如果商家增加了合作的电商平台,比如国美,遂宁,当当,唯品会等等,那么又要改造对应的伪代码。
if(产品信息发生了变化) {
推送到淘宝;
推送到京东;
推送到国美;
推送到当当;
推送到苏宁;
...
}
有的同学读了上面的伪代码之后,觉得还好啊,那是因为我们把推送到各个电商平台的代码都用一行代码来表示成了伪代码。其实在真的开发的时候,我们不可能是一行代码就能搞定。如果我们合作的平台增加,那么代码会越来越庞大,导致我们不可维护。
我们观察者模式,就可以很好的解决这个问题。我们把每一个电商的接口都看做一个观察者,每一个观察者都能观察到产品列表。当公司发布新的产品的时候,就会发送到这个产品列表上。于是产品列表就发生了变化。这个时候会自动的触发各个电商接口(观察者)发送到合作平台上去。
具体流程图:
代码实现:
先做被观察者代码实现,其实被观察者就是我们的产品列表:
package com.vgxit.sjms.ktdm.obser;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
/**
* 定义一个被观察者
* 被观察者需要集成一个类Observable
*/
public class ProductList extends Observable {
//产品列表
private List<String> productList = new ArrayList<>();
//被观察者唯一的单例
private static ProductList PRODUCTLIST;
private ProductList(){}
public static synchronized ProductList getInstance() {
if (PRODUCTLIST == null) {
PRODUCTLIST = new ProductList();
}
return PRODUCTLIST;
}
/**
* 添加产品的方法
* @param product
*/
public void addProduct(String product) {
productList.add(product);
System.out.println("添加了产品:" + product);
//1,设置本对象的状态已经更改了
setChanged();
//2,通知观察者我已经更改了
notifyObservers(product);
}
/**
* 获取所有的商品的字符串
* @return
*/
public String getAllProduct() {
return String.join(",", productList);
}
}
定义观察者,比如淘宝和京东接口:
package com.vgxit.sjms.ktdm.obser;
import java.util.Observable;
import java.util.Observer;
/**
* 淘宝电商接口(观察者),所有的观察者都需要实现Observer接口,如果监听到了有变化直接调用update方法
*/
public class TaobaoObserver implements Observer {
@Override
public void update(Observable o, Object arg) {
String product = (String) arg;
ProductList productList = (ProductList) o;
System.out.println("我是淘宝接口,商品" + product + "被更新了");
System.out.println("当前商户拥有的产品列表有:" + productList.getAllProduct());
}
}
package com.vgxit.sjms.ktdm.obser;
import java.util.Observable;
import java.util.Observer;
/**
* 京东的电商接口(观察者)
*/
public class JdObserver implements Observer {
@Override
public void update(Observable o, Object arg) {
String product = (String) arg;
ProductList productList = (ProductList) o;
System.out.println("我是京东接口,商品" + product + "被更新了");
System.out.println("当前商户拥有的产品列表有:" + productList.getAllProduct());
}
}
测试代码:
package com.vgxit.sjms.ktdm.obser;
public class TestObser {
public static void main(String[] args) {
ProductList productList = ProductList.getInstance();
//为被观察者注册观察者对象
productList.addObserver(new TaobaoObserver());
productList.addObserver(new JdObserver());
//添加商品
productList.addProduct("华为手机");
productList.addProduct("正宗狗粮");
}
}
就是,你只负责你自己的比如更新商品的逻辑,其他的同步到各个平台的逻辑,你不用管。而作为观察者,你也不需要了解别人更新商品的具体业务逻辑是什么,你只需要了解他的确是更新了,就行了。各自维护各自的业务逻辑,相互不干扰。
10,工厂模式:
大部分情况下,我们都是以new关键字的方式来创建对象。但是现实中有很多东西的构建可能是很复杂的。比如我们的电脑,cpu可能是因特尔或者amd设计的,cpu的制造可能是台积电。内存条可能是金士顿的,硬盘可能是西数的。组装可能是富士康。而对于我们购买电脑的程序来说,我不需要关心你这个电脑的生产过程,我只需要买到家之后能够写代码,可以玩游戏就行了。
为了解决上面的问题,我们设计模式中就提出了工厂模式。而工厂模式又分为两种,一种是简单工厂模式,一种是抽象工厂模式。
简单工厂模式
简单工厂模式其实上是非常简单的。我们还是用生产电脑举例。比如我们有一个接口叫做Computer,然后又很多实现类,比如ThinkpadComputer,ShenzhouComputer,AcerComputer。然后我们还有一个工厂类FushikangFacotry专门来生产电脑。然后使用使用简单工厂模式来做一下这个例子。
1,首先创建一个Computer接口:
package com.vgxit.sjms.ktdm.simplefactory;
/**
* 抽象电脑接口
*/
public interface Computer {
//抽象写代码的方法
void coding();
}
2,编写Computer的实现类:
package com.vgxit.sjms.ktdm.simplefactory;
public class ThinkPadComputer implements Computer {
private String name;
public ThinkPadComputer(String name) {
this.name = name;
}
@Override
public void coding() {
System.out.println("Thinkpad:" + name + "编写代码...");
}
}
package com.vgxit.sjms.ktdm.simplefactory;
public class ShenzhouComputer implements Computer {
private String name;
public ShenzhouComputer(String name) {
this.name = name;
}
@Override
public void coding() {
System.out.println("神州:" + name + "编写代码...");
}
}
package com.vgxit.sjms.ktdm.simplefactory;
public class AcerComputer implements Computer {
private String name;
public AcerComputer(String name) {
this.name = name;
}
@Override
public void coding() {
System.out.println("弘基:" + name + "编写代码...");
}
}
3,编写对应的工厂:
package com.vgxit.sjms.ktdm.simplefactory;
public class FushikangFacotry {
public static Computer createComputer(String brandName) {
Computer computer = null;
switch (brandName) {
case "thinkPad":
computer = new ThinkPadComputer("ThinkPad P17");
//此处省略50行的构造代码
break;
case "shenzhou":
computer = new ThinkPadComputer("战神GX10");
break;
case "acer":
computer = new AcerComputer("ConceptD7 Ezel");
break;
}
return computer;
}
}
4,具体调用代码:
package com.vgxit.sjms.ktdm.simplefactory;
public class SImpleFactoryTest {
public static void main(String[] args) {
Computer computer = FushikangFacotry.createComputer("acer");
computer.coding();
}
}
抽象工厂模式
抽象工厂模式可以向客户端提供一个接口,使得客户端在不必指定产品的具体情况下,创建多个产品族中的产品对象。
上面这句话可能很多同学读了之后也没有什么感觉。那我们还是举例说明,比如在我们现实生活中,我们要生产汽车。汽车可能是轿车,大巴车,货运车等等。这些汽车我们肯定是在不同的工厂里面完成的。如果我们都在一个工厂里面生产,对于不同的汽车,有不同的参设和不同的设备,我们都不加以区分的在同一个工厂里面,肯定会造成管理上的混乱。
具体案例:
1,编写一个汽车接口:
package com.vgxit.sjms.ktdm.absfactory;
public interface Car {
void run();
}
2,编写工厂接口,专门用来生产汽车的:
package com.vgxit.sjms.ktdm.absfactory;
public interface ICarFactory {
Car createCar(String productNo);
}
3,编写小汽车实现类:
package com.vgxit.sjms.ktdm.absfactory;
public class SmallCar implements Car {
private String productNo;
public SmallCar(String productNo) {
this.productNo = productNo;
}
@Override
public void run() {
System.out.println("用" + productNo + "载上心爱的女孩子,一起兜风。");
}
}
4,编写小汽车生产工厂:
package com.vgxit.sjms.ktdm.absfactory;
public class SmallCarFactory implements ICarFactory {
@Override
public Car createCar(String productNo) {
return new SmallCar(productNo);
}
}
5,编写集团工厂:
package com.vgxit.sjms.ktdm.absfactory;
public class HuaweiCarFacotry implements ICarFactory {
@Override
public Car createCar(String productNo) {
ICarFactory carFactory = null;
if (productNo.startsWith("small")) {
carFactory = new SmallCarFactory();
}
return carFactory.createCar(productNo);
}
}
6,测试代码:
package com.vgxit.sjms.ktdm.absfactory;
public class AbsFactoryTest {
public static void main(String[] args) {
ICarFactory carFactory = new HuaweiCarFacotry();
Car car = carFactory.createCar("small-2021-nvshen");
car.run();
}
}
11,建造者模式:
建造者模式是日常开发中比较常见的设计模式,它的主要作用就是将复杂事物创建的过程抽象出来,该抽象的不同实现方式不同,创建出的对象也不同。通俗的讲,创建一个对象一般都会有一个固定的步骤,这个固定的步骤我们把它抽象出来,每个抽象步骤都会有不同的实现方式,不同的实现方式创建出的对象也将不同。举个常见的例子,想必大家都买过组装电脑,电脑的生产或者组装其实就是属于建造者模式,我们知道,电脑的生产都需要安装CPU、内存条、硬盘等元器件。我们可以把这个安装步骤抽象出来,至于到底装哪种CPU,比如i5还是i7就是对该抽象安装步骤的具体实现。
建造者模式分为两种,一种为经典建造者模式,另一种为变种建造者模式。我们来挨个看下:
我们先来看看建造者模式的UML类图:
从上图可以看到,经典Buider模式中有四个角色:
- 要建造的产品Product -- 组装的电脑
- 抽象的Builder -- 装CPU、内存条、硬盘等抽象的步骤
- Builder的具体实现ConcreteBuilder -- 对上述抽象步骤的实现,比如装i5CPU、8G内存条、1T硬盘
- 使用者Director -- 电脑装机人员
1,创建一个Computer类:
package com.vgxit.sjms.ktdm.builder;
public class Computer {
private String cpu;
private String memory;
private String hardDisk;
private String graphicsCard;
private String keybord;
private String mouese;
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public String getMemory() {
return memory;
}
public void setMemory(String memory) {
this.memory = memory;
}
public String getHardDisk() {
return hardDisk;
}
public void setHardDisk(String hardDisk) {
this.hardDisk = hardDisk;
}
public String getGraphicsCard() {
return graphicsCard;
}
public void setGraphicsCard(String graphicsCard) {
this.graphicsCard = graphicsCard;
}
public String getKeybord() {
return keybord;
}
public void setKeybord(String keybord) {
this.keybord = keybord;
}
public String getMouese() {
return mouese;
}
public void setMouese(String mouese) {
this.mouese = mouese;
}
@Override
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", memory='" + memory + '\'' +
", hardDisk='" + hardDisk + '\'' +
", graphicsCard='" + graphicsCard + '\'' +
", keybord='" + keybord + '\'' +
", mouese='" + mouese + '\'' +
'}';
}
}
2,创建一个抽象组装电脑的Builder类:
package com.vgxit.sjms.ktdm.builder;
/**
* 这个类就是把组装电脑的具体步骤抽象出来
*/
public interface ComputerConfigBuilder {
void setCpu();
void setMemory();
void setHardDisk();
void setGraphicsCard();
void setKeybord();
void setMouse();
}
3,有了抽象的组装过程,接下来我们就需要创建具体的组装的实现类。我们知道电脑一般都有高配和低配。我们这里就创建一个低配的LowConfigBuilder和一个高配的HighConfigBuilder
package com.vgxit.sjms.ktdm.builder;
public class LowConfigBuilder implements ComputerConfigBuilder {
private Computer computer;
public LowConfigBuilder() {
this.computer = new Computer();
}
@Override
public void setCpu() {
computer.setCpu("i5");
}
@Override
public void setMemory() {
computer.setMemory("8G");
}
@Override
public void setHardDisk() {
computer.setHardDisk("500G");
}
@Override
public void setGraphicsCard() {
computer.setGraphicsCard("NADIV 2G");
}
@Override
public void setKeybord() {
computer.setKeybord("薄膜键盘");
}
@Override
public void setMouse() {
computer.setMouese("有线鼠标");
}
@Override
public Computer getComputer() {
return computer;
}
}
package com.vgxit.sjms.ktdm.builder;
public class HighConfigBuilder implements ComputerConfigBuilder {
private Computer computer;
public HighConfigBuilder() {
this.computer = new Computer();
}
@Override
public void setCpu() {
computer.setCpu("i7");
}
@Override
public void setMemory() {
computer.setMemory("32G");
}
@Override
public void setHardDisk() {
computer.setHardDisk("1T");
}
@Override
public void setGraphicsCard() {
computer.setGraphicsCard("NADIV 4G");
}
@Override
public void setKeybord() {
computer.setKeybord("机械键盘");
}
@Override
public void setMouse() {
computer.setMouese("逻辑无线鼠标");
}
@Override
public Computer getComputer() {
return computer;
}
}
4,上面我们已经定义好了低配和高配的两种方案,接下来我们还需要一个装机人员
package com.vgxit.sjms.ktdm.builder;
public class Director {
private ComputerConfigBuilder builder;
public void setBuilder(ComputerConfigBuilder builder) {
this.builder = builder;
}
private void createComputer() {
builder.setCpu();
builder.setGraphicsCard();
builder.setHardDisk();
builder.setKeybord();
builder.setMemory();
builder.setMouse();
}
public Computer getComputer() {
createComputer();
return builder.getComputer();
}
}
5,演示创建电脑,我们高配和低配都试试看
低配演示:
package com.vgxit.sjms.ktdm.builder;
public class BuilderTest {
public static void main(String[] args) {
//装机人员
Director director = new Director();
//告诉装机人员,我们使用低配组装电脑
director.setBuilder(new LowConfigBuilder());
//装机人员装机并返回装好了的电脑
Computer computer = director.getComputer();
System.out.println(computer);
}
}
高配的演示:
package com.vgxit.sjms.ktdm.builder;
public class BuilderTest {
public static void main(String[] args) {
//装机人员
Director director = new Director();
//告诉装机人员,我们使用高配组装电脑
director.setBuilder(new HighConfigBuilder());
//装机人员装机并返回装好了的电脑
Computer computer = director.getComputer();
System.out.println(computer);
}
}