设计模式
设计模式代表了最佳的实践 通常被有经验的面向对象的软件开发人员所采用 设计模式是开发人员在软件开发过程中面临的一般问题的解决方案 这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的
设计模式是一套被反复使用的 多数人知晓的 经过分类编目的 代码设计经验的总结 使用设计模式是为了重用代码 让代码更容易被他人理解 保证代码可靠性 毫无疑问 设计模式于己于他人于系统都是多赢的 设计模式使代码编制真正工程化 设计模式是软件工程的基石 项目中合理地运用设计模式可以完美地解决很多问题 每种模式在现实中都有相应的原理来与之对应 每种模式都描述了一个在我们周围不断重复发生的问题 以及该问题的核心解决方案 这也是设计模式能被广泛应用的原因
设计模式的类型
根据设计模式的参考书Design Patterns - Element of Reusable Object-Oriened Software(设计模式 - 可服用的面向对象软件元素)中所提到的 总共有23种设计模式 这些设计模式可以分为三大类:
- 创建型模式(Creational Patterns)
- 结构型模式(Structural Patterns)
- 行为型模式(Behavioral Patterns)
- J2EE 设计模式
| 序号 | 模式 & 描述 | 包括 |
|---|---|---|
| 1 | 创建型模式 这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式 而不是使用new运算符直接实例化对象 这使得程序在判断针对某个给定实例化需要创建哪些对象时更加灵活 | |
| 2 | 结构型模式这些模式关注对象之间的组合和关系 旨在解决如何构建灵活且可复用的类和对象结构 | |
| 3 | 行为型模式 这些模式关注对象之间的通信和交互 旨在解决对象之间的责任分配和算法的封装 | |
| 4 | J2EE模式这些设计模式特别关注表示层 这些模式是由Sun Java Center鉴定的 |
设计模式的优点
- 提供了一种共享的设计词汇和概念 使开发人员能够更好地沟通和理解彼此的设计意图
- 提供了经过验证的解决方案 可以提高软件的可维护性 可复用性和灵活性
- 促进了代码的复用 避免了重复的设计和实现
- 通过遵循设计原则 可以减少系统中的错误和问题 提高代码质量
设计模式的六大原则
1.开闭原则
对扩展开放 对修改关闭 在程序需要进行拓展的时候 不能去修改原有的代码 实现一个热插拔的效果
2.里氏代换原则LSP
是面向对象设计的基本原则之一 任何基类可以出现的地方 子类一定可以出现 LSP是继承复用的基石 只有当派生类可以替换掉基类 且软件单位的功能不受到影响时 基类才可以真正被复用 而派生类也能够在基类的基础上增加新的行为 LSP是对开闭原则的补充 实现开闭原则的关键步骤就是抽象化 而基类与子类的继承关系就是抽象化的具体实现
3.依赖倒转原则
这个原则是开闭原则的基础 具体内容:针对接口编程 依赖于抽象而不依赖于具体
4.接口隔离原则
使用多个隔离的接口 比使用单个接口要好 降低类之间的耦合度
5.迪米特法则 又称最少知道原则
一个实体应当尽量少地与其他实体之间发生相互作用 使得系统功能模块相对独立
6.合成复用原则
尽量使用合成/聚合的方式 而不是使用继承
工厂模式
工厂模式是Java中最常用的设计模式之一 他提供了一种创建对象的方式 使得创建对象的过程与使用对象的过程分离 创建对象而无需指定要创建的具体类 可以将对象的创建逻辑封装在一个工厂类中 而不是在客户端代码中实例化对象 这样可以提高代码的可维护性和可扩展性
工厂模式的类型
- 简单工厂模式
- 简单工厂模式不是一个正式的设计模式 但它是工厂模式的基础 它使用一个单独的工厂类来创建不同的对象 根据传入的参数决定创建哪种类型的对象
- 工厂方法模式
- 工厂方法模式定义了一个创建对象的接口 但由子类决定实例化哪个类 工厂方法将对象的创建延迟到子类
- 抽象工厂模式
- 抽象工厂模式提供一个创建一系列相关或互相依赖对象的接口 而无需指定它们具体的类
概要
使用场景
- 日志记录: 日志可能记录到本地硬盘 系统事件 远程服务器等 用户可以选择记录日志的位置
- 数据库访问: 当用户不知道最终系统使用哪种数据库 或者数据库可能变化时
- 连接服务器的框架设计: 需要支持"POP3" "IMAP" "HTTP" 三种协议 可以将这三种协议作为产品类 共同实现一个接口
结构
- 抽象产品: 定义了产品的共同接口或抽象类 它可以是具体产品类的父类或接口 规定了产品对象的共同方法
- 具体产品: 实现了抽象产品接口 定义了具体产品的特定行为和属性
- 抽象工厂: 声明了创建产品的抽象方法 可以是接口或抽象类 它可以有多个方法用于创建不同类型的产品
- 具体工厂: 实现了抽象工厂接口 负责实际创建具体产品的对象
实现
我们将创建一个Shape接口和实现Shape接口的实体类 下一步是定义工厂类ShapeFactory
FactoryPatternDemo类使用ShapeFactory来获取Shape对象 它将向ShapeFactory传递信息 以便获取它所需对象的类型
创建一个接口 Shape.java
public interface Shape {
void draw();
}
创建实现接口的实体类 Rectangle.java
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
Square.java
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
Circle.java
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
创建一个工厂 生成基于给定信息的实体类的对象 ShapeFactory.java
public class ShapeFactory {
public Shape getShape(String shapeType) {
if (shapeType == null) {
return null;
}
if (shapeType.equalsIgnoreCase("CIRCLE")) {
return new Circle();
}
else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
return new Rectangle();
}
else if (shapeType.equalsIgnoreCase("SQUARE")) {
return new Square();
}
return null;
}
}
使用该工厂 通过传递类型信息来会哦去实体类的对象 FactoryPatternDemo.java
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
Shape shape1 = shapeFActory.getShape("CIRCLE");
shape1.draw();
Shape shape2 = shapeFactory.getShape("RECTANGLE");
shape2.draw();
Shape shape3 = shapeFactory.getShape("SQUARE");
shape3.draw();
}
}
抽象工厂模式
抽象工厂模式是围绕一个超级工厂创建其他工厂 该超级工厂又称为其他工厂的工厂 这种类型的设计模式属于创建型模式 它提供了一种创建对象的最佳方式 在抽象工厂模式中 接口是负责创建一个相关对象的工厂 不需要显式指定它们的类 每个生成的工厂都能按照工厂模式提供对象 抽象工厂模式提供了一种创建一系列相关或相互依赖对象的接口 而无需指定具体实现类 通过使用抽象工厂模式 可以将客户端与具体产品的创建过程解耦 使得客户端可以通过工厂接口来创建一族产品
概要
意图
提供一个创建一系列相关或相互依赖对象的接口 而无需指定它们的具体类
应用实例
假设有不同类型的衣柜 每个衣柜(具体工厂)只能存放一类衣服(成套的具体产品) 如商务装 时尚装等 每套衣服包括具体的上衣和裤子(具体产品) 所有衣柜都是衣柜类(抽象工厂)的具体实现 所有上衣和裤子分别实现上衣接口和裤子接口(抽象产品)
结构
- 抽象工厂 Abstract Factory 声明了一组用于创建产品对象的方法 每个方法对应一种产品类型 抽象工厂可以是接口或抽象类
- 具体工厂 Concrete Factory 实现了抽象工厂接口 负责创建具体产品对象的实例
- 抽象产品 Abstract Product 定义了一组产品对象的共同接口或抽象类 描述了产品对象的公共方法
- 具体产品 Concrete Product 实现了抽象产品接口 定义了具体产品的特定行为和属性
实现
我们将创建Shape和Color接口和实现这些接口的实体类 下一步是创建抽象工厂类AbstractFactory 接着定义工厂类ShapeFactory和ColorFactory 这两个工厂类都是扩展了AbstractFactory 然后创建一个工厂创造器/生成器类FactoryProducer
AbstractFactoryPatternDemo类使用FactoryProducer来获取AbstractFactory对象 它将向AbstractFactory传递形状信息Shape(CIRCLE/RECTANGLE/SQUARE) 以便获取它所需对象的类型 同时它还向AbstractFactory传递颜色信息Color(RED/GREEN/BLUE) 以便获取它所需对象的类型
为形状创建一个接口 Shape.java
public interface Shape {
void draw();
}
创建实现接口的实体类 Rectangle.java
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
Square.java
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
Circle.java
public class Circle implemens Shape {
@Override
public void draw () {
System.out.println("Inside Circle::draw() method.");
}
}
为颜色创建一个接口 Color.java
public interface Color{
void fill();
}
创建实现接口的实体类 Red.java
public class Red implements Color {
@Override
public void fill() {
System.out.println("Inside Red::fill() method.");
}
}
Green.java
public class Green implements Color {
@Override
public void fill() {
System.out.println("Inside Green::fill() method.");
}
}
Blue.java
public class Blue implements Color {
@Override
public voide fill() {
System.out.println("Inside Blue::fill() method.");
}
}
为Color和Shape对象创建抽象类来获取工厂 AbstractFactory.java
public abstract class AbstractFactory {
public abstract Color getColor(String color);
public abstract Shape getShape(String shape);
}
创建扩展了AbstractFactory的工厂类 给予给定的信息生成实体类的对象 ShapeFactory.java
public class ShapeFactory extends AbstractFActory {
@Override
public Shape getShape(String shapeType) {
if(shapeType == null) {
return null;
}
if (shapeType.equalsIgnoreCase("CIRCLE")) {
return new Circle();
} else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
return new Rectangle();
} else if (shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
@Override
public Color getColor(String color) {
retun null;
}
}
ColorFactory.java
public class ColorFactory extends AbstractFactory {
@Override
public Shape getShape(String shapeType) {
return null;
}
@Override
public Color getColor(String color) {
if(color == null) {
return null;
}
if (color.equalsIgnoreCase("RED")) {
return new Red();
} else if(color.equalsIgnoreCase("GREEN")) {
return new Green();
} else if (color.equalsIgnoreCase("BLUE")) {
return new Blue();
}
return null;
}
}
创建一个工厂创造器/生成器类 通过传递形状或颜色信息来获取工厂 FactoryProducer.java
public class FactoryProducer {
public static AbstractFactory getFactory(String choice) {
if (choice.equalsIgnoreCase("SHAPE")){
return new ShapeFactory();
} else if (choice.equalsIgnoreCase("COLOR")){
return new ColorFactory();
}
return null;
}
}
使用FactoryProducer来获取AbstractFactory 通过传递类型信息来获取实体类的对象 AbstractFactoryPatternDemo.java
public class AbstractFactoryPatternDemo {
public static void main(String[] args) {
// 获取形状工厂
AbstractFactory shapeFactory = FactoryProducer.getFactory("SHAPE");
// 获取形状为Circle的对象
Shape shape1 = shapeFactory.getShape("CIRCLE");
// 调用Circle的draw方法
shape1.draw();
// 获取形状为Rectangle的对象
Shape shape2 = shapeFactory.getShape("RECTANGLE");
// 调用Rectangle的draw方法
shape2.draw();
// 获取形状为Square的对象
Shape shape3 = shapeFactory.getShape("SQUARE");
// 调用Square的draw方法
shape3.draw();
// 获取颜色工厂
AbstractFactory colorFactory = FactoryProducer.getFActory("COLOR");
// 获取颜色为Red的对象
Color color1 = colorFactory.getColor("RED");
// 调用Red的fill方法
color1.fill();
// 获取颜色为Green的对象
Color color2 = colorFactory.getColor("GREEN");
// 调用Green的fill方法
color2.fill();
// 获取颜色为Blue的对象
Color color3 = colorFactory.getColor("BLUE");
color3.fill();
}
}
单例模式
单例模式(Singleton Pattern)是Java中最简单的设计模式之一 这种类型的设计模式属于创建型模式 它提供了一种创建对象的最佳方式
这种模式涉及到一个单一的类 该类负责创建自己的对象 同时确保只有单个对象呗创建 这个类提供了一种访问其唯一的对象的方式 可以直接访问 不需要实例化该类的对象
单例模式是一种创建型设计模式 它确保一个类只有一个实例 并提供了一个全局访问点来访问该实例
注意:
- 单例类只能有一个实例
- 单例类必须自己创建自己的唯一实例
- 单例类必须给所有其他对象提供这一示例
概要
意图
确保一个类只有一个实例 并提供一个全局访问点来访问该实例
主要解决
频繁创建和销毁全局使用的类实例的问题
何时使用
当需要控制实例数目 节省系统资源时
如何解决
检查系统是否已经存在该单例 如果存在则返回该实例 如果不存在则创建一个新实例
关键代码
构造函数式私有的
优点
- 内存中只有一个实例 减少内存开销 尤其是频繁创建和销毁实例时
- 避免资源的多重占用
缺点
- 没有接口 不能继承
- 与单一职责原则冲突 一个类应该只关心内部逻辑 而不关心实例化方式
使用场景
- 生成唯一序列号
- WEB中的计数器 避免每次刷新都在数据库中增加计数 先缓存起来
- 创建消耗资源过多的对象 如I/O与数据库连接等
注意事项
- 线程安全: getInstance()方法中需要使用同步锁synchronized(Singleton.class)防止多线程同时进入造成实例被多次创建
- 延迟初始化: 实例在第一次调用getInstance()方法时被创建
- 序列化和反序列化: 重写readResolve方法以确保反序列化时不会创建新的实例
- 反射攻击: 在构造函数中添加防护代码 防止通过反射创建新实例
- 类加载器问题: 注意复杂类加载环境可能导致的多个实例问题
结构
单例模式包含一下几个主要角色:
- 单例类: 包含单例实例的类 通常将构造函数声明为私有
- 静态成员变量: 用于存储单例实例的静态成员变量
- 获取实例方法: 静态方法 用于获取单例实例
- 私有构造函数: 防止外部直接实例化单例类
- 线程安全处理: 确保在多线程环境下单例实例的创建是安全的
实现
我们将创建一个SingleObject类 SingleObject类有它的私有构造函数和本身的一个静态实例 SingleObject类提供了一个静态方法 供外界获取它的静态实例 SinglePatternDemo类使用SingleObject类来获取SingleObject对象
创建一个Singleton类 SingleObject.java
public class SingleObject {
// 创建 SingleObject的一个对象
private static SingleObject instance = new SingleObject();
// 让构造函数为private 这样该类就不会被实例化
private Singleobject() {}
// 获取唯一可用的对象
public static SingleObject getInstance() {
return instance;
}
public void showMessage() {
System.out.println("Hello World!");
}
}
从singleton类获取唯一的对象 SingletonPatternDemo.java
public class SingletonPatternDemo {
public static void main(String[] args) {
// 不合法的构造函数
// 编译时错误: 构造函数 SingleObject() 是不可见的
// SingleObject object = new SingleObject();
// 获取唯一可用的对象
SingleObject object = SingleObject.getInstance();
// 显示消息
object.showMessage()
}
}
单例模式的几种实现方式
1.懒汉式 现成不安全
是否Lazy初始化: 是
是否多线程安全: 否
实现难度: 易
描述: 这种方式是最基本的实现方式 这种实现最大的问题就是不支持多线程 因为没有加锁 synchronized 所以严格意义上它并不算单例模式
这种lazy loading很明显 不要球线程安全 在多线程不能正常工作
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
2.懒汉式 线程安全
是否Lazy初始化: 是
是否多线程安全: 是
实现难度: 易
描述: 这种方式具备很好的lazy loading 能够在多线程中很好的工作 但是效率很低 99%情况下爱不需要同步
优点: 第一次调用才初始化 避免内存浪费
缺点: 必须加锁sunchronized才能保证单例 但是加锁会影响效率
getInstance()的性能对应用程序不是很关键 (该方法使用不太频繁)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance(){
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
MVC模式
MVC模式代表Model-View-Controller(模型-视图-控制器)模式 这种模式用于应用程序的分层开发
- Model 模型 模型代表一个存取数据的对象或JAVA POJO 他可以带有逻辑 在数据变化时更新控制器
- View 视图 视图代表模型包含的数据的可视化
- Controller 控制器 控制器作用于模型和视图上 它控制数据流向模型对象 并在数据变化时更新视图 它使视图与模型分离
概要
意图
将应用程序分为三个核心组件 模型 视图 和 控制器 以实现关注点分离
主要解决的问题
解决了应用程序中业务逻辑 数据和界面显示的耦合问题 使得开发和维护更加清晰和简单
使用场景
当需要将数据 业务逻辑和界面显示分离 以便于独立开发和维护时
实现方式
- 模型Model 负责数据和业务逻辑 通常包含数据存储 检索和业务规则
- 视图 View 负责显示数据的用户界面 不包含业务逻辑
- 控制器 Controller 接受用户的输入 调用模型和视图去完成用户的请求
关键代码
- 模型 包含业务逻辑和数据状态
- 视图 包含展示逻辑 将模型的数据渲染为用户界面
- 控制器 包含逻辑 用于响应用户输入并更新模型和视图
应用实例
- Web应用程序 用户通过浏览器发送请求 服务端的控制器处理请求 模型进行数据处理
实现
我们将创建一个座作为模型的Student对象 StudentView是一个把学生详细信息输出到控制台的视图类 StudentController是负责存储数据到Student对象的控制器类 并相应地更新视图StudentView
MVCPatternDemo 我们演示类使用StudentController来演示MVC模式的用法
创建模型 Student.java
public class Student {
private String rollNo;
private String name;
public String getRollNo() {
return rollNo;
}
public void setRollNo(String rollNo) {
this.rollNo = rollNo;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
创建视图 StudentView.java
public class StudentView {
public void printStudentDetails(String studentName, String studentRollNo) {
System.out.println("Student: ");
System.out.println("Name: " + studentName);
System.out.println("Roll No: " + studentRollNo);
}
}
创建控制器 StudentController.java
public class StudentController {
private Student model;
private StudentView view;
public StudentController(Student model, StudentView view) {
this.model = model;
this.view = view;
}
public void setStudentName (String name) {
model.setName(name);
}
public String getStudentName() {
return model.getName();
}
public void setStudentRollNo(String rollNo) {
model.rollNo(rollNo);
}
public String getStudentRollNo() {
return model.rollNo();
}
public void updateView() {
view.printStudentDetails(model.getName(), model.getRollNo());
}
}
使用StudentController方法来演示MVC设计模式的用法 MVCPatternDemo.java
public class MVCPatternDemo {
public static void main(String[] args) {
Student model = retrieveStudentFromDatabase();
StudentView view = new StudentView();
StudentController controller = new StudentController(model, view);
controller.updataView();
controller.setStudentName("John");
controller.updateView();
}
}
private static Student retrieveStudentFromDatabase() {
Student student = new Student();
student.setName("Robert");
student.setRollNo("10");
return student;
}