访问者模式 Visitor
1、什么是访问者模式
访问者模式允许定义一些不改变数据结构的前提下的操作。通过这种方式,可以在不修改元素类的情况下定义新的操作。访问者模式常用于对复杂对象结构进行操作,而又不希望在这些对象上破坏封装性。
2、为什么使用访问者模式
- 访问者模式将数据结构和操作分离,使得新增操作更加灵活,而不影响数据结构。
- 可以通过定义新的访问者来增加新的操作,而无需修改元素类,可扩展性强
3、如何使用访问者模式
设计实现一个图形编辑器,包含不同图形类型和对应的不同的操作
// 抽象元素类
interface Shape {
void accept(Visitor visitor);
}
// 具体元素类1:圆形
class Circle implements Shape {
private int radius;
Circle(int radius) {
this.radius = radius;
}
public int getRadius() {
return radius;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 具体元素类2:矩形
class Rectangle implements Shape {
private int width;
private int height;
Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 抽象访问者
interface Visitor {
void visit(Circle circle);
void visit(Rectangle rectangle);
}
// 具体访问者1:移动操作
class MoveVisitor implements Visitor {
private int deltaX;
private int deltaY;
MoveVisitor(int deltaX, int deltaY) {
this.deltaX = deltaX;
this.deltaY = deltaY;
}
@Override
public void visit(Circle circle) {
System.out.println("Moving circle with radius " + circle.getRadius() + " by (" + deltaX + ", " + deltaY + ")");
}
@Override
public void visit(Rectangle rectangle) {
System.out.println("Moving rectangle with width " + rectangle.getWidth() + " and height " + rectangle.getHeight() + " by (" + deltaX + ", " + deltaY + ")");
}
}
// 具体访问者2:缩放操作
class ScaleVisitor implements Visitor {
private double scaleFactor;
ScaleVisitor(double scaleFactor) {
this.scaleFactor = scaleFactor;
}
@Override
public void visit(Circle circle) {
System.out.println("Scaling circle with radius " + circle.getRadius() + " by factor " + scaleFactor);
}
@Override
public void visit(Rectangle rectangle) {
System.out.println("Scaling rectangle with width " + rectangle.getWidth() + " and height " + rectangle.getHeight() + " by factor " + scaleFactor);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Shape circle = new Circle(5);
Shape rectangle = new Rectangle(10, 8);
Visitor moveVisitor = new MoveVisitor(2, 3);
Visitor scaleVisitor = new ScaleVisitor(1.5);
// 执行移动操作
circle.accept(moveVisitor);
rectangle.accept(moveVisitor);
// 执行缩放操作
circle.accept(scaleVisitor);
rectangle.accept(scaleVisitor);
}
}
4、是否存在缺陷和不足
如果系统中新增了一个元素类,所有的具体访问者类都需要修改,不符合开闭原则。
5、如何缓解缺陷和不足
可以通过引入抽象访问者,将新增元素类的访问方法定义在抽象访问者中,然后具体访问者类只需要实现必要的访问方法。
观察者模式 Observer
1、什么是观察者模式
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听一个主题对象。当主题对象状态发生变化时,所有依赖它的观察者都会得到通知并自动更新。
2、为什么使用观察者模式
- 观察者模式实现了发布者和订阅者之间的松耦合,使得它们可以独立变化,不影响彼此。
- 新的观察者可以随时加入,而不影响发布者的实现,可扩展性强。
3、如何使用观察者模式
设计实现一个简单的新闻订阅系统,其中有多个订阅者订阅不同类型的新闻
import java.util.ArrayList;
import java.util.List;
// 主题接口
interface Subject {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers(String news);
}
// 具体主题:新闻发行
class NewsPublisher implements Subject {
private List<Observer> observers;
NewsPublisher() {
this.observers = new ArrayList<>();
}
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers(String news) {
for (Observer observer : observers) {
observer.update(news);
}
}
}
// 观察者接口
interface Observer {
void update(String news);
}
// 具体观察者1:普通订阅者
class RegularSubscriber implements Observer {
private String name;
RegularSubscriber(String name) {
this.name = name;
}
@Override
public void update(String news) {
System.out.println(name + " received regular news: " + news);
}
}
// 具体观察者2:紧急订阅者
class UrgentSubscriber implements Observer {
private String name;
UrgentSubscriber(String name) {
this.name = name;
}
@Override
public void update(String news) {
System.out.println(name + " received urgent news: " + news);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建新闻发行主题
NewsPublisher newsPublisher = new NewsPublisher();
// 创建订阅者
Observer regularSubscriber1 = new RegularSubscriber("Alice");
Observer regularSubscriber2 = new RegularSubscriber("Bob");
Observer urgentSubscriber = new UrgentSubscriber("Charlie");
// 订阅者订阅主题
newsPublisher.addObserver(regularSubscriber1);
newsPublisher.addObserver(regularSubscriber2);
newsPublisher.addObserver(urgentSubscriber);
// 发布新闻
newsPublisher.notifyObservers("Important news: COVID-19 vaccine breakthrough!");
newsPublisher.notifyObservers("Regular news: Weather forecast for the week");
// 移除一个订阅者
newsPublisher.removeObserver(regularSubscriber2);
// 再次发布新闻
newsPublisher.notifyObservers("Urgent news: Emergency evacuation due to natural disaster!");
}
}
4、是否存在缺陷和不足
- 在观察者模式中,观察者的更新顺序是不确定的,可能导致一些依赖顺序的问题。
- 通知模式比较单一,通知观察者的方式是同步的,如果某个观察者的更新操作耗时较长,可能影响整体性能。
5、如何缓解缺陷和不足
- 可以使用队列将观察者的更新操作异步执行,避免影响主题的通知效率。
- 可以引入顺序控制机制,明确观察者的更新顺序。