设计模式
设计模式
设计模式是指在软件设计中经常遇到的一些重复性问题所提供的可复用解决方案,它可以帮助我们提高软件的可维护性、可扩展性和可重用性。Java中的设计模式是基于面向对象编程思想而产生的,它们可以帮助我们更好地组织代码并降低代码的耦合度。
基本原则
Java中的设计模式基于以下基本原则:
- 单一职责原则
- 开放封闭原则
- 里氏替换原则
- 依赖倒置原则
- 接口隔离原则
- 迪米特法则
常见的设计模式
Java中常见的设计模式包括:
- 工厂模式
- 单例模式
- 原型模式
- 适配器模式
- 装饰器模式
- 代理模式
- 观察者模式
- 责任链模式
- 命令模式
- 模板方法模式
- 策略模式
设计模式是面向对象编程中非常重要的一个概念,它可以帮助我们更好地组织代码并提高代码的可维护性和可扩展性。在实际应用中,我们应该根据需要选择合适的设计模式来解决问题,避免过度使用设计模式导致代码复杂度增加。
基本原则介绍
单一职责原则
单一职责原则(SRP)是指一个类或模块只负责完成一个职责或功能。如果一个类或模块承担了多个职责或功能,那么它的职责和功能就会变得不清晰,容易出现bug,并且难以维护和扩展。
Java示例代码:
public class UserService {
public void register(User user) {
// 注册用户
}
public void login(User user) {
// 登录用户
}
public void updateUser(User user) {
// 更新用户信息
}
public void deleteUser(User user) {
// 删除用户
}
}
在上面的示例代码中,UserService
类负责用户的注册、登录、更新和删除这四个职责,这违反了单一职责原则。我们可以将其拆分为四个类,每个类只负责一个职责。
开放封闭原则
开放封闭原则(OCP)是指一个软件实体应该对扩展开放,对修改关闭。也就是说,在不修改原有代码的情况下,通过扩展它来实现新的功能。
Java示例代码:
public interface Shape {
void draw();
}
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Drawing rectangle");
}
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing circle");
}
}
public class ShapeDrawer {
private List shapes = new ArrayList<>();
public void addShape(Shape shape) {
shapes.add(shape);
}
public void drawShapes() {
for (Shape shape : shapes) {
shape.draw();
}
}
}
在上面的示例代码中,Shape
接口表示图形,Rectangle
和Circle
类分别实现了Shape
接口。ShapeDrawer
类负责绘制图形,其中addShape
方法用于添加图形,drawShapes
方法用于绘制所有图形。如果我们需要新增一种图形,只需要创建一个新的类,实现Shape
接口,并添加到ShapeDrawer
中即可,不需要修改原有代码。
里氏替换原则
里氏替换原则(LSP)是指在任何可以使用基类对象的地方,一定可以使用子类对象来替换。也就是说,子类应该能够替换掉父类并且不会影响程序的正确性。
Java示例代码:
public class Vehicle {
public void run() {
System.out.println("Vehicle is running");
}
}
public class Car extends Vehicle {
@Override
public void run() {
System.out.println("Car is running");
}
}
public class Bike extends Vehicle {
@Override
public void run() {
System.out.println("Bike is running");
}
}
在上面的示例代码中,Vehicle
类表示车辆,Car
和Bike
类分别继承自Vehicle
类。由于Car
和Bike
类继承自Vehicle
类,所以它们可以替换Vehicle
类的对象,并且不会影响程序的正确性。
依赖倒置原则
依赖倒置原则(DIP)是指高层模块不应该依赖于低层模块,它们应该依赖于抽象。同时,抽象不应该依赖于具体实现,具体实现应该依赖于抽象。
Java示例代码:
public interface MessageSender {
void send(String message);
}
public class EmailSender implements MessageSender {
@Override
public void send(String message) {
System.out.println("Sending email: " + message);
}
}
public class SmsSender implements MessageSender {
@Override
public void send(String message) {
System.out.println("Sending SMS: " + message);
}
}
public class NotificationService {
private MessageSender messageSender;
public NotificationService(MessageSender messageSender) {
this.messageSender = messageSender;
}
public void sendNotification(String message) {
messageSender.send(message);
}
}
在上面的示例代码中,MessageSender
接口表示消息发送器,EmailSender
和SmsSender
类分别实现了MessageSender
接口。NotificationService
类表示通知服务,其中的sendNotification
方法可以发送通知,它通过MessageSender
接口与具体的消息发送器解耦。这样,我们可以在不修改NotificationService
类的情况下,通过传递不同的MessageSender
对象来实现不同的消息发送方式。
接口隔离原则
接口隔离原则(ISP)是指一个类对另一个类的依赖应该建立在最小的接口上。也就是说,不应该强迫一个类依赖于它不需要的方法。
Java示例代码:
public interface Payment {
void pay();
}
public class CreditCardPayment implements Payment {
@Override
public void pay() {
System.out.println("Paying with credit card");
}
}
public class CashPayment implements Payment {
@Override
public void pay() {
System.out.println("Paying with cash");
}
}
public class ShoppingCart {
private List payments = new ArrayList<>();
public void addPayment(Payment payment) {
payments.add(payment);
}
public void checkout() {
for (Payment payment : payments) {
payment.pay();
}
}
}
在上面的示例代码中,Payment
接口表示支付方式,CreditCardPayment
和CashPayment
类分别实现了Payment
接口。ShoppingCart
类表示购物车,其中的addPayment
方法用于添加支付方式,checkout
方法用于结账。由于ShoppingCart
类依赖于Payment
接口,所以我们可以根据需要添加不同的支付方式,而不需要强迫ShoppingCart
类依赖于它不需要的方法。
迪米特法则
迪米特法则(LKP)是指一个对象应该对其他对象有最少的了解。也就是说,一个对象应该尽量减少与其他对象之间的交互,只和自己的直接朋友交互。
Java示例代码:
public class Teacher {
private String name;
public Teacher(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class Course {
private Teacher teacher;
public Course(Teacher teacher) {
this.teacher = teacher;
}
public void printTeacherName() {
System.out.println(teacher.getName());
}
}
public class School {
private List courses = new ArrayList<>();
public void addCourse(Course course) {
courses.add(course);
}
public void printCourseTeacherNames() {
for (Course course : courses) {
course.printTeacherName();
}
}
}
在上面的示例代码中,Teacher
类表示教师,Course
类表示课程,School
类表示学校。由于Course
类只依赖于Teacher
类,而不依赖于School
类,所以我们可以在不修改Course
类的情况下,将其添加到School
类中,并调用printCourseTeacherNames
方法打印所有课程的教师名字。
Java中常见的设计模式
工厂模式
工厂模式是一种创建型设计模式,它提供了一种创建对象的方式,而无需暴露对象的创建逻辑。工厂模式有多种实现方式,最常见的有简单工厂模式、工厂方法模式、抽象工厂模式。
简单工厂模式
简单工厂模式是最简单的工厂模式,它将对象的创建逻辑封装在一个工厂类中,客户端只需要调用工厂类的静态方法来获取对象实例。以下是一个简单工厂模式的Java代码示例:
// 抽象产品类
public interface Product {
void use();
}
// 具体产品类A
public class ConcreteProductA implements Product {
@Override
public void use() {
System.out.println("Using product A.");
}
}
// 具体产品类B
public class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("Using product B.");
}
}
// 工厂类
public class SimpleFactory {
public static Product createProduct(String type) {
switch (type) {
case "A":
return new ConcreteProductA();
case "B":
return new ConcreteProductB();
default:
throw new IllegalArgumentException("Invalid product type.");
}
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Product productA = SimpleFactory.createProduct("A");
Product productB = SimpleFactory.createProduct("B");
productA.use(); // Using product A.
productB.use(); // Using product B.
}
}
在这个示例中,工厂类SimpleFactory提供了一个静态方法createProduct()来创建产品实例,客户端只需要调用这个方法并传入产品类型参数即可。在createProduct()方法中,根据不同的产品类型返回不同的具体产品实例。
工厂方法模式
工厂方法模式是一种更加灵活的工厂模式,它将对象的创建延迟到子类中实现,客户端只需要针对抽象工厂和抽象产品编程,而无需知道具体的实现类。以下是一个工厂方法模式的Java代码示例:
// 抽象产品类
public interface Product {
void use();
}
// 具体产品类A
public class ConcreteProductA implements Product {
@Override
public void use() {
System.out.println("Using product A.");
}
}
// 具体产品类B
public class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("Using product B.");
}
}
// 抽象工厂类
public interface Factory {
Product createProduct();
}
// 具体工厂类A
public class ConcreteFactoryA implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
// 具体工厂类B
public class ConcreteFactoryB implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Factory factoryA = new ConcreteFactoryA();
Factory factoryB = new ConcreteFactoryB();
Product productA = factoryA.createProduct();
Product productB = factoryB.createProduct();
productA.use(); // Using product A.
productB.use(); // Using product B.
}
}
在这个示例中,抽象工厂类Factory定义了一个抽象方法createProduct(),具体工厂类ConcreteFactoryA和ConcreteFactoryB分别实现了这个方法来创建具体产品实例。客户端代码针对抽象工厂和抽象产品编程,通过具体工厂类的实例来创建具体产品实例。
抽象工厂模式
抽象工厂模式是一种提供多个工厂方法的工厂模式,它可以创建一组相关或相互依赖的对象。抽象工厂模式通常需要定义多个抽象产品类和多个抽象工厂类,每个抽象工厂类负责创建一组相关的具体产品。以下是一个抽象工厂模式的Java代码示例:
// 抽象产品类Apublic interface ProductA {
void use();
}
// 具体产品类A1public class ConcreteProductA1 implements ProductA {
@Override
public void use() {
System.out.println("Using product A1.");
}
}
// 具体产品类A2public class ConcreteProductA2 implements ProductA {
@Override
public void use() {
System.out.println("Using product A2.");
}
}
// 抽象产品类Bpublic interface ProductB {
void eat();
}
// 具体产品类B1public class ConcreteProductB1 implements ProductB {
@Override
public void eat() {
System.out.println("Eating product B1.");
}
}
// 具体产品类B2public class ConcreteProductB2 implements ProductB {
@Override
public void eat() {
System.out.println("Eating product B2.");
}
}
// 抽象工厂类public interface Factory {
ProductA createProductA();
ProductB createProductB();
}
// 具体工厂类1public class ConcreteFactory1 implements Factory {
@Override
public ProductA createProductA() {
return new ConcreteProductA1();
}
@Override
public ProductB createProductB() {
return new ConcreteProductB1();
}
}
// 具体工厂类2public class ConcreteFactory2 implements Factory {
@Override
public ProductA createProductA() {
return new ConcreteProductA2();
}
@Override
public ProductB createProductB() {
return new ConcreteProductB2();
}
}
// 客户端代码public class Client {
public static void main(String[] args) {
Factory factory1 = new ConcreteFactory1();
Factory factory2 = new ConcreteFactory2();
ProductA productA1 = factory1.createProductA();
ProductB productB1 = factory1.createProductB();
ProductA productA2 = factory2.createProductA();
ProductB productB2 = factory2.createProductB();
productA1.use();// Using product A1.
productB1。eat();// Eating product B1.
productA2.use();// Using product A2.
productB2.eat();// Eating product B2.
}
}
在这个示例中,抽象工厂类Factory定义了两个抽象方法createProductA()和createProductB(),具体工厂类ConcreteFactory1和ConcreteFactory2分别实现了这两个方法来创建一组相关的具体产品。客户端代码通过具体工厂类的实例来创建具体产品实例,每个工厂类只创建自己对应的产品实例。
单例模式
单例模式是一种创建型设计模式,它保证一个类只有一个实例,并提供了一个全局访问点供外部代码获取该实例。单例模式通常需要满足三个条件:私有化构造函数、提供一个静态方法获取实例、保证只有一个实例存在。
懒汉式
以下是一个使用懒汉式实现的单例模式的Java代码示例:
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
在这个示例中,Singleton类的构造函数是私有的,这意味着外部代码不能通过实例化Singleton类来创建新的对象。Singleton类提供了一个静态方法getInstance()来获取Singleton类的唯一实例。在getInstance()方法中,如果还没有创建实例,就会创建一个新实例并返回,否则直接返回已有的实例。
需要注意的是,这种懒汉式实现的单例模式在多线程环境下可能会存在线程安全问题。可以通过加锁或使用双重检查锁定等方式来解决这个问题。
饿汉式
在这种实现方式中,单例对象在类加载的时候就已经创建好了,因此不存在线程安全问题,但是可能会浪费一些内存空间。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
双重检查锁定
这种实现方式通过双重检查锁定来保证线程安全和性能,它延迟了对象的创建时间,只有在第一次访问单例对象时才创建实例。
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
静态内部类
这种实现方式通过静态内部类来延迟单例对象的创建时间,它既保证了线程安全,又避免了饿汉式实现方式的内存浪费问题。
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
枚举
这种实现方式利用枚举的特性来保证单例对象的唯一性,它既简单又安全。
public enum Singleton {
INSTANCE;
public void doSomething() {
// ...
}
}
原型模式
原型模式是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而无需通过实例化来创建对象。原型模式在创建复杂对象的场景下非常有用,可以避免重复创建相同的对象,提高对象创建的效率。
以下是一个原型模式的Java代码示例:
public abstract class Prototype implements Cloneable {
public abstract Prototype clone();
}
public class ConcretePrototype extends Prototype {
private String field1;
private int field2;
public ConcretePrototype(String field1, int field2) {
this.field1 = field1;
this.field2 = field2;
}
public String getField1() {
return field1;
}
public void setField1(String field1) {
this.field1 = field1;
}
public int getField2() {
return field2;
}
public void setField2(int field2) {
this.field2 = field2;
}
@Override
public Prototype clone() {
return new ConcretePrototype(field1, field2);
}
}
在这个示例中,Prototype是原型类,定义了一个抽象方法clone(),该方法用于复制原型对象。ConcretePrototype是具体原型类,实现了原型类的clone()方法,并定义了自己的属性和方法。
在工业软件开发中,原型模式常常用于对象的复制和初始化。例如,在制造业中,产品的制造需要经历一系列的工艺流程,每个工艺流程都需要对产品进行加工和处理。如果每次都重新创建一个产品对象,那么就会浪费很多时间和资源。通过原型模式,可以在第一次创建产品对象时,将其复制为原型对象,并在每个工艺流程中复制原型对象,然后对复制后的对象进行加工和处理,这样就可以避免重复创建对象的问题,提高了加工效率。另外,在设计CAD软件时,也可以使用原型模式来实现图形对象的复制和粘贴功能。当用户选择复制一个图形对象时,可以将该对象作为原型对象,并将其复制到剪贴板中。当用户选择粘贴操作时,可以从剪贴板中获取原型对象,并将其复制一份作为新的图形对象,然后将其插入到画布中。这样就可以避免重复创建相同的图形对象,提高了图形操作的效率。
适配器模式
适配器模式是一种结构型设计模式,它允许不兼容的接口之间进行协作。适配器模式通过创建一个中间适配器来转换一个接口为另一个接口,从而使得原本不兼容的接口能够协同工作。
以下是一个适配器模式的Java代码示例:
public interface Target {
void request();
}
public class Adaptee {
public void specificRequest() {
// ...
}
}
public class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
在这个示例中,Target是目标接口,定义了一个request()方法。Adaptee是被适配的类,其中包含一个specificRequest()方法。Adapter是适配器类,它实现了Target接口,并包含一个Adaptee对象。在Adapter的request()方法中,调用Adaptee对象的specificRequest()方法来完成目标接口的请求。
常见的使用场景:
- 旧接口与新接口的适配:当系统需要使用一个老接口但是只有新接口可用时,可以使用适配器模式将新接口转换为老接口。这种情况通常发生在系统的升级和改进中。
- 外部接口与内部接口的适配:当系统需要使用一个外部接口,但是该接口与内部接口不兼容时,可以使用适配器模式将外部接口转换为内部接口。这种情况通常发生在系统与第三方库或服务集成时。
- 多个接口之间的适配:当系统需要使用多个不兼容的接口进行协同工作时,可以使用适配器模式将这些接口转换为统一的接口。这种情况通常发生在系统需要与多个外部系统协同工作时。
总之,适配器模式可以帮助我们在不改变现有系统接口的情况下,实现不同接口之间的协同工作,提高系统的可扩展性和灵活性。
装饰器模式
装饰器模式是一种结构型设计模式,允许你通过将对象放入包装对象中来为原对象添加新的行为。装饰器模式可以在不修改原始对象的情况下,动态地添加功能。
Java代码示例:
// 定义一个接口public interface Shape {
void draw();
}
// 创建一个实现接口的实体类public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Shape: Rectangle");
}
}
// 创建一个装饰器类,实现同样的接口public abstract class ShapeDecorator implements Shape {
protected Shape decoratedShape;
public ShapeDecorator(Shape decoratedShape){
this.decoratedShape = decoratedShape;
}
public void draw(){
decoratedShape.draw();
}
}
// 创建扩展了 ShapeDecorator 类的实体装饰类public class RedShapeDecorator extends ShapeDecorator {
public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
}
@Override
public void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
}
private void setRedBorder(Shape decoratedShape){
System.out.println("Border Color: Red");
}
}
// 使用装饰器public class DecoratorPatternDemo {
public static void main(String[] args) {
Shape rectangle = new Rectangle();
Shape redRectangle = new RedShapeDecorator(new Rectangle());
System.out.println("Normal Rectangle:");
rectangle.draw();
System.out.println("\nRed Rectangle:");
redRectangle.draw();
}
}
在软件开发中,装饰器模式通常应用于以下场景:
- 当需要动态地为对象添加功能或行为时,可以使用装饰器模式。这种方式比继承更加灵活,因为它可以在运行时动态地添加或删除功能。
- 当不能采用继承方式对对象进行扩展时,可以使用装饰器模式。例如,当类已经被定义为final时,就无法通过继承方式进行扩展,此时可以使用装饰器模式来添加新的行为。
- 当需要在不影响其他对象的情况下,对单个对象进行修改或扩展时,可以使用装饰器模式。这种方式避免了在修改对象时对其他对象造成影响。
- 在 GUI 程序设计中,装饰器模式常用于为控件添加新的行为或外观。例如,可以使用装饰器模式为按钮添加边框、背景颜色等效果,而不需要对按钮本身进行修改。
代理模式
代理模式是一种结构型设计模式,它允许你提供一个代理对象,以控制对其它对象的访问。
在代理模式中,代理对象拥有与实际对象相同的接口,客户端无需知道实际对象的存在,只需要与代理对象进行交互。代理对象在需要时将请求转发给实际对象,并在必要时执行一些额外的逻辑。
下面是一个简单的Java代码示例:
interface Image {
void display();
}
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk(filename);
}
public void display() {
System.out.println("Displaying " + filename);
}
private void loadFromDisk(String filename) {
System.out.println("Loading " + filename);
}
}
class ProxyImage implements Image {
private RealImage realImage;
private String filename;
public ProxyImage(String filename) {
this.filename = filename;
}
public void display() {
if (realImage == null) {
realImage = new RealImage(filename);
}
realImage.display();
}
}
public class Main {
public static void main(String[] args) {
Image image = new ProxyImage("test.jpg");
image.display();
}
}
在这个示例中,Image
是一个接口,RealImage
是实现它的具体类,ProxyImage
是实现了Image
接口的代理类。
代理类在display()
方法中检查实际对象是否已经被创建,如果没有,它将创建一个并调用其display()
方法。如果实际对象已经存在,代理类将直接调用它的display()
方法。
代理模式在软件开发中的场景举例:
- 远程代理:在客户端和服务器之间使用远程代理,客户端通过远程代理访问服务器上的对象。
- 虚拟代理:在必要时,使用虚拟代理来延迟实际对象的创建,以提高性能和减少资源消耗。例如,当需要加载大型图像或视频文件时,可以使用虚拟代理来避免在初始化时加载所有数据。
- 安全代理:在访问敏感对象时,使用安全代理来控制对对象的访问。例如,只有经过身份验证的用户才能访问某些对象。
- 缓存代理:在访问频繁的对象时,使用缓存代理来缓存对象的结果,避免重复计算。例如,在计算复杂数学函数时,可以使用缓存代理来存储已经计算过的结果,以提高性能。
- 日志记录代理:在访问对象时,使用日志记录代理来记录访问日志,以便后续分析和调试。例如,在调试时,可以使用日志记录代理来记录对象的访问和参数。
观察者模式
观察者模式是一种行为型设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会收到通知并自动更新。
以下是一个观察者模式的Java代码示例:
public interface Observer {
void update(int data);
}
public interface Subject {
void attach(Observer observer);
void detach(Observer observer);
void notifyObservers();
}
public class ConcreteSubject implements Subject {
private List observers = new ArrayList<>();
private int data;
@Override
public void attach(Observer observer) {
observers.add(observer);
}
@Override
public void detach(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(data);
}
}
public void setData(int data) {
this.data = data;
notifyObservers();
}
}
public class ConcreteObserver implements Observer {
private int data;
@Override
public void update(int data) {
this.data = data;
System.out.println("Observer received data: " + data);
}
}
在这个示例中,Observer是观察者接口,定义了一个update()方法。Subject是主题接口,定义了attach()、detach()和notifyObservers()方法。ConcreteSubject是具体主题类,实现了Subject接口,并包含一个观察者列表和一个数据字段。在ConcreteSubject中,当数据发生变化时,通过notifyObservers()方法通知所有的观察者。ConcreteObserver是具体观察者类,实现了Observer接口,并在update()方法中打印出接收到的数据。
观察者模式在软件开发过程中的场景举例:
- GUI组件
在GUI组件中,用户界面通常需要根据某些状态或事件的变化来更新界面。例如,在一个文本编辑器中,当用户输入文本时,文本区域和状态栏都需要实时更新。这时,可以使用观察者模式来实现状态和界面之间的同步。
- 数据库操作
在数据库操作中,当数据库中的某些数据发生变化时,需要及时通知其他系统或组件进行相应的处理。例如,在一个电商网站中,当用户下单购买商品时,需要更新库存和订单信息。这时,可以使用观察者模式来实现库存和订单之间的同步。
- 网络通信
在网络通信中,当一个节点的状态发生变化时,需要及时通知其他节点进行相应的处理。例如,在一个分布式系统中,当一个节点发生故障时,其他节点需要及时接管该节点的任务和数据。这时,可以使用观察者模式来实现节点之间的同步和协同工作。
总之,观察者模式可以帮助我们实现对象之间的松耦合和同步,提高系统的可维护性和可扩展性。
责任链模式
责任链模式是一种行为型设计模式,它允许多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
Java代码示例:
// 定义一个抽象处理器public abstract class AbstractLogger {
public static int INFO = 1;
public static int DEBUG = 2;
public static int ERROR = 3;
protected int level;
// 责任链中的下一个元素protected AbstractLogger nextLogger;
public void setNextLogger(AbstractLogger nextLogger){
this.nextLogger = nextLogger;
}
public void logMessage(int level, String message){
if(this.level <= level){
write(message);
}
if(nextLogger !=null){
nextLogger.logMessage(level, message);
}
}
abstract protected void write(String message);
}
// 创建扩展了 AbstractLogger 的实体类public class ConsoleLogger extends AbstractLogger {
public ConsoleLogger(int level){
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("Standard Console::Logger: " + message);
}
}
public class ErrorLogger extends AbstractLogger {
public ErrorLogger(int level){
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("Error Console::Logger: " + message);
}
}
public class FileLogger extends AbstractLogger {
public FileLogger(int level){
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("File::Logger: " + message);
}
}
// 使用责任链模式public class ChainPatternDemo {
public static void main(String[] args) {
AbstractLogger loggerChain = getChainOfLoggers();
loggerChain.logMessage(AbstractLogger.INFO, "This is an information.");
loggerChain.logMessage(AbstractLogger.DEBUG, "This is a debug level information.");
loggerChain.logMessage(AbstractLogger.ERROR, "This is an error information.");
}
private static AbstractLogger getChainOfLoggers(){
AbstractLogger errorLogger = newErrorLogger(AbstractLogger.ERROR);
AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
errorLogger.setNextLogger(fileLogger);
fileLogger.setNextLogger(consoleLogger);
return errorLogger;
}
}
场景举例:
在软件开发中,责任链模式通常应用于以下场景:
- 当需要将请求发送给多个对象时,可以使用责任链模式。例如,一个异常可以被多个异常处理程序处理,且每个处理程序只负责处理特定类型的异常。
- 当不确定哪个对象应该处理请求时,可以使用责任链模式。例如,一个请求可能需要根据不同的条件由不同的处理程序处理。
- 当需要动态地添加或删除处理程序时,可以使用责任链模式。这种方式可以让用户添加或删除处理程序,而不需要修改代码。
- 当需要将请求从一个对象传递到另一个对象时,可以使用责任链模式。例如,在一个分布式系统中,可以使用责任链模式将请求从一个节点传递到另一个节点,直到找到能够处理请求的节点为止。
命令模式
命令模式是一种行为型设计模式,它允许你将请求封装为一个对象,并将其传递给不同的对象进行调用。
在命令模式中,命令对象封装了一个请求和相关的参数,可以将其传递给调用者,调用者无需知道请求的具体实现,只需要调用命令对象的执行方法即可。
下面是一个简单的Java代码示例:
interface Command {
void execute();
}
class Light {
public void turnOn() {
System.out.println("Light is on");
}
public void turnOff() {
System.out.println("Light is off");
}
}
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.turnOn();
}
}
class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
public void execute() {
light.turnOff();
}
}
class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
public class Main {
public static void main(String[] args) {
Light light = new Light();
Command lightOnCommand = new LightOnCommand(light);
Command lightOffCommand = new LightOffCommand(light);
RemoteControl remoteControl = new RemoteControl();
remoteControl.setCommand(lightOnCommand);
remoteControl.pressButton();
remoteControl.setCommand(lightOffCommand);
remoteControl.pressButton();
}
}
在这个示例中,Command
是一个接口,LightOnCommand
和LightOffCommand
实现了它。RemoteControl
类持有一个Command
对象,并在调用pressButton()
方法时执行该对象的execute()
方法。
命令模式在软件开发中的场景举例:
- 网络请求队列:在处理网络请求时,可以将请求封装为命令对象,将其加入到请求队列中,然后异步执行。这可以减少网络请求的延迟和提高系统的响应速度。
- 撤销和重做功能:在实现撤销和重做功能时,可以使用命令模式来记录操作历史,每个命令对象都可以记录其执行前和执行后的状态,并实现撤销和重做方法。
- 菜单系统:在实现菜单系统时,可以将每个菜单项封装为命令对象,然后将其添加到菜单中。当用户选择菜单项时,菜单对象将调用相应的命令对象。
- 日志记录:在记录系统操作日志时,可以使用命令模式来记录每个操作,包括其执行时间、执行者和执行结果等信息。
- 队列系统:在实现队列系统时,可以将每个任务封装为命令对象,并将其加入到队列中。队列系统将异步执行每个任务,并且可以动态添加或删除任务。
模板方法
模板方法模式是一种行为型设计模式,它定义了一个算法的骨架,将算法中不变的部分抽象出来,而将可变的部分留给子类来实现。模板方法模式使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。
以下是一个模板方法模式的Java代码示例:
public abstract class AbstractClass {
public final void templateMethod() {
operation1();
operation2();
if (hook()) {
operation3();
}
}
protected abstract void operation1();
protected abstract void operation2();
protected void operation3() {}
protected boolean hook() {
return true;
}
}
public class ConcreteClass1 extends AbstractClass {
@Override
protected void operation1() {
System.out.println("ConcreteClass1: operation1");
}
@Override
protected void operation2() {
System.out.println("ConcreteClass1: operation2");
}
}
public class ConcreteClass2 extends AbstractClass {
@Override
protected void operation1() {
System.out.println("ConcreteClass2: operation1");
}
@Override
protected void operation2() {
System.out.println("ConcreteClass2: operation2");
}
@Override
protected void operation3() {
System.out.println("ConcreteClass2: operation3");
}
@Override
protected boolean hook() {
return false;
}
}
在这个示例中,AbstractClass是抽象模板类,定义了一个templateMethod()方法,其中包含了若干个操作步骤。operation1()和operation2()是抽象方法,需要由子类实现。operation3()是一个可选的钩子方法,可以由子类覆盖。hook()是一个钩子方法,用于控制模板方法中的某些流程。ConcreteClass1和ConcreteClass2是具体子类,实现了抽象方法和钩子方法。
模板方法模式在软件开发过程中的场景举例:
- 框架设计
在框架设计中,通常需要定义一些通用的算法流程,并允许用户根据自己的需求定制某些具体的算法步骤。这时,可以使用模板方法模式来定义算法的骨架,将可变的部分留给用户来实现。
- 测试框架
在测试框架中,通常需要执行一系列的测试用例,并对测试结果进行统计和分析。这时,可以使用模板方法模式来定义测试用例的执行流程,将测试用例的具体实现留给子类来完成。
- 数据库操作
在数据库操作中,通常需要执行一些通用的数据库操作步骤,并允许用户根据自己的需求定制某些具体的操作步骤。这时,可以使用模板方法模式来定义数据库操作的骨架,将可变的部分留给用户来实现。
总之,模板方法模式可以帮助我们实现算法的重用和扩展,提高系统的可维护性和可扩展性。
策略模式
策略模式是一种行为型设计模式,它允许在运行时选择算法的行为。策略模式定义了一系列算法,将每个算法封装起来,并使它们可以相互替换。选择哪种算法由客户端决定。
Java代码示例:
// 定义一个接口
public interface Strategy {
int doOperation(int num1, int num2);
}
// 创建实现接口的实体类
public class OperationAdd implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
public class OperationSubtract implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
public class OperationMultiply implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}
// 创建 Context 类
public class Context {
private Strategy strategy;
public Context(Strategy strategy){
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2){
return strategy.doOperation(num1, num2);
}
}
// 使用策略模式
public class StrategyPatternDemo {
public static void main(String[] args) {
Context context = new Context(new OperationAdd());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationSubtract());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationMultiply());
System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
}
}
在软件开发中,策略模式通常应用于以下场景:
- 当需要在运行时选择算法时,可以使用策略模式。例如,在一个游戏中,玩家可以选择不同的策略来战胜不同的敌人,每个敌人可能有不同的弱点和强点。
- 当需要避免使用大量的条件语句时,可以使用策略模式。例如,在一个电商网站中,可以根据不同的促销策略来计算折扣,而不需要使用大量的if-else语句。
- 当需要将算法的实现与使用算法的客户端分离时,可以使用策略模式。这种方式可以使得算法的实现可以独立于客户端进行修改和扩展,而不会影响客户端的代码。
- 当需要在不同的环境下使用不同的算法时,可以使用策略模式。例如,在一个移动设备上,可以根据设备的处理能力来选择不同的算法实现。