设计模式原则
- 开闭原则 对扩展开放,对修改关闭。
- 依赖倒置原则 通过抽象使各个类或者模块不相互影响,实现松耦合。
- 单一职责原则一个类、接口、方法只做一件事。
- 接口隔离原则 尽量保证接口的纯洁性,客户端不应该依赖不需要的接口。
- 迪米特法则又叫最少知道原则,一个类对其所依赖的类知道得越少越好。
- 里氏替换原则 子类可以扩展父类的功能但不能改变父类原有的功能。
- 合成复用原则尽量使用对象组合、聚合,而不使用继承关系达到代码复用的目的。
简单工厂模式
这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
缺点扩展性不好 违反了开闭原则 对修改关闭
工厂类
可以看到我们工厂类提供了创建的方法 有三种 都是创建不同的产品的方法
/**
* @Author jyl
* @Date 2021/12/9 16:18
* @Version 1.0
* 简单工厂类
*/
public class ICourseFactory {
/*第一版本的创建模式有弊端 因为无法检测name*/
// public ICourse create(String name){
// if("java".equals(name)){
// return new JavaCourse();
// }else if("python".equals(name)){
// return new PythonCourse();
// }else {
// return null;
// }
// }
/*第二版根据全类名来创建*/
// public ICourse create(String className){
// try {
// if (!(null == className || "".equals(className))) {
// return (ICourse) Class.forName(className).newInstance();
// }
//
// }catch (Exception e){
// e.printStackTrace();
// }
// return null;
// }
/*第三版使用反射创建*/
public ICourse create(Class<? extends ICourse> clazz){
try {
if (null != clazz) {
return clazz.newInstance();
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
产品类
这里看到我们有一个产品类型是录课 我们两个具体产品 录java课程和 录python课程 他们都实现了录课 他们都有录课方法只是录课的内容不同 也就是具体的实现类不同 这里的打印 录制java 录制python 代表的是不同产品 的不同逻辑产品类通用接口
这样做的坏处就是 我们每次扩展一个代码就必须去修改我们的工厂类 因为有这个问题所有 出现了 工厂方法模式
/**
* @Author jyl
* @Date 2021/12/9 16:14
* @Version 1.0
* 录制视频接口
*/
public interface ICourse {
/**
* 录制视频
* @return
*/
void record();
}
java课程
/**
* @Author jyl
* @Date 2021/12/9 16:15
* @Version 1.0
* 子类
*/
public class JavaCourse implements ICourse {
public void record() {
System.out.println("录制Java课程");
}
}
python课程
/**
* @Author jyl
* @Date 2021/12/9 16:15
* @Version 1.0
* 子类
*/
public class PythonCourse implements ICourse {
public void record() {
System.out.println("录制Python课程");
}
}
客户端调用
/**
* @Author jyl
* @Date 2021/12/9 16:21
* @Version 1.0
* 简单工厂客户端
*/
public class SimpleFactoryTest {
public static void main(String[] args) {
/*不使用工厂配置*/
// ICourse course = new JavaCourse();
// course.record();
// /*第一版配置*/
// ICourseFactory factory = new ICourseFactory();
// ICourse course = factory.create("java");
// course.record();
/*第二版工厂配置使用全类名*/
// ICourseFactory factory = new ICourseFactory();
// ICourse course = factory.create("yongli.product.impl.JavaCourse");
// course.record();
ICourseFactory factory = new ICourseFactory();
ICourse course = factory.create(JavaCourse.class);
course.record();
/*日期类jdk使用简单工厂模式案例*/
Calendar.getInstance();
/*slf4j 使用简单工厂案例*/
LoggerFactory.getLogger(SimpleFactoryTest.class);
}
Calendar.getInstance 使用简单工厂模式的例子
LoggerFactory.getLogger(); 使用简单工厂模式的例子
这里switch case 简单工厂
简单工厂方法模式
可以看到这次把工厂抽象把每个产品的创建分担到了每个具体的工厂 这个时候一定有和我一样的人再想都这样了 为啥不能直接new对象了 可以想一下 工厂模式解决的是创建时候的难题 比如我构造这个对象有很多setget 特定的创建方式 有些东西不想暴露给客户端 所以产生了这种这个工厂使用太单一,一种工厂只能生产一种产品
抽象工厂接口
/**
* @Author jyl
* @Date 2021/12/9 17:14
* @Version 1.0
* 工厂方法模式接口
*/
public interface ICourseFactory {
ICourse create();
}
实现工厂接口的具体工厂
/**
* @Author jyl
* @Date 2021/12/9 17:15
* @Version 1.0
* 方法工厂模式分担责任的产品创建工厂这里用来创建产品
*/
public class JavaCourseFactory implements ICourseFactory {
public ICourse create() {
return new JavaCourse();
}
}
/**
* @Author jyl
* @Date 2021/12/9 17:15
* @Version 1.0
*/
public class PythonCourseFactory implements ICourseFactory {
public ICourse create() {
return new PythonCourse();
}
}
客户端调用
/**
* @Author jyl
* @Date 2021/12/9 16:21
* @Version 1.0
* 方法工厂客户端
*/
public class FactorymethodTest {
public static void main(String[] args) {
ICourseFactory iCourseFactory = new JavaCourseFactory();
iCourseFactory.create().record();
}
}
抽象工厂模式
抽象工厂模式(Abastract Factory Pattern)是指提供一个创建一系列相关或相互依赖 对象的接口,无须指定他们具体的类。客户端(应用层)不依赖于产品类实例如何被创 建、实现等细节,强调的是一系列相关的产品对象(属于同一产品族)一起使用创建对 象需要大量重复的代码。需要提供一个产品类的库,所有的产品以同样的接口出现,从 而使客户端不依赖于具体实现。
产品族的概念
以前我们每次都是新增一个产品现在我们是每次新增为一个产品族 比如以前我们使用 有录课工厂 就要实现 java录课工厂类 和python录课工厂类 笔记工厂就要新增笔记工厂类和 java笔记和 python笔记 现在我们 直接把他们抽取出来 变成一个综合的工厂 叫做 java课程超级工厂 相当于 java笔记工厂和 java 笔记工厂的融合,这个时候python超级工厂也一样
就算以后出golang c++ 也只是新增一个超级工厂 。 这些超级工厂又因为是一个产品族 所以抽象为一个接口供大家继承
从上图中看出有正方形,圆形和菱形三种图形,相同颜色深浅的就代表同一个产品族, 相同形状的代表同一个产品等级结构。同样可以从生活中来举例,比如,美的电器生产 多种家用电器。那么上图中,颜色最深的正方形就代表美的洗衣机、颜色最深的圆形代 表美的空调、颜色最深的菱形代表美的热水器,颜色最深的一排都属于美的品牌,都是 美的电器这个产品族。再看最右侧的菱形,颜色最深的我们指定了代表美的热水器,那 么第二排颜色稍微浅一点的菱形,代表海信的热水器。同理,同一产品结构下还有格力 热水器,格力空调,格力洗衣机。
再看下面的这张图,最左侧的小房子我们就认为具体的工厂,有美的工厂,有海信工厂, 有格力工厂。每个品牌的工厂都生产洗衣机、热水器和空调。
顶层抽象工厂
/**
* @Author jyl
* @Date 2021/12/9 19:57
* @Version 1.0
* 最顶层抽象工厂模式
* 每次创建一个新的产品族就新增一个创建方法
*/
public interface CourseFactory {
/*创建笔记产品*/
INote createINote();
/*创建录课产品*/
IVideo createIVideo();
}
java课程产品族工厂
/**
* @Author jyl
* @Date 2021/12/9 20:06
* @Version 1.0
* java产品族的工厂类实现抽象工厂接口
*/
public class JavaCourseFactory implements CourseFactory {
public JavaINoteImpl createINote() {
return new JavaINoteImpl();
}
public IVideo createIVideo() {
return new JavaIVideoImpl();
}
}
产品等级结构接口
类别这来就是一个产品等级结构
/**
* @Author jyl
* @Date 2021/12/9 20:00
* @Version 1.0
* 课堂笔记产品族
*/
public interface INote {
/*写笔记*/
void edit();
}
/**
* @Author jyl
* @Date 2021/12/9 20:00
* @Version 1.0
*/
/*录课产品族的接口*/
public interface IVideo {
/*录视频*/
void record();
}
java产品的具体实现
/**
* @Author jyl
* @Date 2021/12/9 20:05
* @Version 1.0
* java笔记产品
*/
public class JavaINoteImpl implements INote {
public void edit() {
System.out.println("我在写java笔记");
}
}
/**
* @Author jyl
* @Date 2021/12/9 20:06
* @Version 1.0
* java录课产品
*/
public class JavaIVideoImpl implements IVideo {
/*录视频*/
public void record() {
System.out.println("我是录java视频");
}
}
java客户端调用
/**
* @Author jyl
* @Date 2021/12/13 8:50
* @Version 1.0
*/
public class AbstractFactoryTest {
public static void main(String[] args) {
CourseFactory factory = new JavaCourseFactory();
/*创建笔记产品*/
factory.createINote().edit();
/*创建录课产品*/
factory.createIVideo().record();
}
}
新增一个python产品种族
通过以上带我们我们可以穿件 java 产品的一个产品种族了
我们下面要新增一个python产品种族
python课程产品族工厂
/**
* @Author jyl
* @Date 2021/12/13 8:54
* @Version 1.0
*
* python产品族工厂
*/
public class PythonCourseFactory implements CourseFactory {
public INote createINote() {
return new PythonINoteImpl();
}
public IVideo createIVideo() {
return new JavaIVideoImpl();
}
}
Python产品的具体实现
/**
* @Author jyl
* @Date 2021/12/13 8:55
* @Version 1.0
*/
public class PythonINoteImpl implements INote {
public void edit() {
System.out.println("写python笔记");
}
}
/**
* @Author jyl
* @Date 2021/12/13 8:56
* @Version 1.0
*/
public class PythonIVideoImpl implements IVideo {
public void record() {
System.out.println("python录课");
}
}
可以可看到我们新增一个产品族 只需要实现抽象工厂 和其中定义好的产品结构就实现了一个产品种族
新增一个产品
现在我们不止有录课 写笔记了 我们还需要看源码 所以我们要增加一个产品结构那就是 看源码 ISource
我们 此刻我们的抽象工厂改变 新增了一个产品
抽象工厂类改变
/**
* @Author jyl
* @Date 2021/12/9 19:57
* @Version 1.0
* 最顶层抽象工厂模式
* 每次创建一个新的产品族就新增一个创建方法
*/
public interface CourseFactory {
/*创建笔记产品*/
INote createINote();
/*创建录课产品*/
IVideo createIVideo();
/*创建看源码产品*/
ISource createISource();
}
工厂的改变
/**
* @Author jyl
* @Date 2021/12/9 20:06
* @Version 1.0
* java产品族的工厂类实现抽象工厂接口
*/
public class JavaCourseFactory implements CourseFactory {
public JavaINoteImpl createINote() {
return new JavaINoteImpl();
}
public IVideo createIVideo() {
return new JavaIVideoImpl();
}
public ISource createISource() {
return new JavaISourceImpl();
}
}
/**
* @Author jyl
* @Date 2021/12/13 8:54
* @Version 1.0
*
* python产品族工厂
*/
public class PythonCourseFactory implements CourseFactory {
public INote createINote() {
return new PythonINoteImpl();
}
public IVideo createIVideo() {
return new JavaIVideoImpl();
}
public ISource createISource() {
return new PythonISourceImpl();
}
}
新增的产品接口
/**
* @Author jyl
* @Date 2021/12/13 9:06
* @Version 1.0
* 源码产品结构
*/
public interface ISource {
void look();
}
新增具体产品实现类
/**
* @Author jyl
* @Date 2021/12/13 9:12
* @Version 1.0
* java源码产品
*/
public class JavaISourceImpl implements ISource {
public void look() {
System.out.println("我在看Java源码");
}
}
/**
* @Author jyl
* @Date 2021/12/13 9:13
* @Version 1.0
* python产品
*/
public class PythonISourceImpl implements ISource {
public void look() {
System.out.println("我在看python源码");
}
}
上面的代码完整地描述了两个产品族 Java 课程和 Python 课程,也描述了两个产品等级 视频和笔记。抽象工厂非常完美清晰地描述这样一层复杂的关系。但是,不知道大家有 没有发现,如果我们再继续扩展产品等级,将源码 Source 也加入到课程中,那么我们的 代码从抽象工厂,到具体工厂要全部调整,很显然不符合开闭原则。
因此抽象工厂也是 有缺点的:
1、规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂 的接口。
2、增加了系统的抽象性和理解难度。 但在实际应用中,我们千万不能犯强迫症甚至有洁癖。在实际需求中产品等级结构升级 是非常正常的一件事情。我们可以根据实际情况,只要不是频繁升级,可以不遵循开闭 原则。代码每半年升级一次或者每年升级一次又有何不可呢?