在 Java 开发中,父子组件/类之间的有效数据共享至关重要。在本文中,我们将探讨无缝集成的最简单的方法。通过利用这种技术,我们简化了通信并增强了代码组织和模块化,使应用程序更具可扩展性和可维护性。
我们有多种方法来解决这个问题:
- 共享状态或上下文:
在父组件和子组件之间共享公共状态或上下文可以实现数据共享。组件之间可以读取和修改共享数据,从而使它们能够有效地交换信息。通信不是实时的。
现实生活场景:假设您正在构建任务管理应用程序,用户创建、关闭、分类和协作处理任务。您的目标是让经理实时了解当前未完成任务的数量。
适用场景:当父子组件之间通信简单且共享数据简单
- 回调:
回调是子组件通过传递对函数或方法的引用来与父组件进行通信的一种方式。父组件定义回调方法,子组件在需要时调用它。这对于异步操作或事件处理非常有用,或者换句话说,就是想要在事件发生时同时对其做出反应时。
现实生活场景:如果希望在触发数据库连接的 onClose() 方法时输出日志。
适用场景:用于简单的父子交互,特别是当组件之间存在一对一关系时
- 观察者模式:
观察者模式是一种行为设计模式,它在需要实时通信的对象之间建立一对多的依赖关系。在此模式中,父组件(主题)维护子组件(观察者)的列表,并通知它们状态更改。
现实生活场景:假设您正在构建一个股票市场监控系统,用户希望接收特定公司股票价格的实时更新。您还希望能够动态订阅和取消订阅,接收有关特定股票和每个股票的更新观察者对股票价格的变化做出独立反应。
适用场景:当您需要对订阅和取消订阅过程或关系进行更多控制时,此模式对于实现事件很有用,主题和观察者之间的关系是动态的,观察者可能需要在运行时添加或删除,或者希望观察者定义自己的自定义逻辑来响应更新,或者您不希望通过添加第三方来使系统过于复杂。
- 事件和事件监听器:
Java 的事件驱动编程模型允许父组件和子组件使用事件和事件监听器进行通信。子组件可以触发事件,父组件可以监听这些事件并做出相应的响应。当需要更大的灵活性和可扩展性或多个组件需要响应同一事件时,事件和侦听器会发挥作用。
现实生活场景:假设您正在构建一个带有测量温度的传感器的简单温度监控系统。您希望不同的组件能够实时对温度变化做出反应,而不是紧密耦合,这种就派上了用途。
使用场景:事件适用于多个组件有兴趣对系统中的特定事件或更改做出反应的场景。常用于图形用户界面、Web 开发、实时系统和事件驱动架构。要使用事件,可能需要使用一些第三方来帮助,例如 Redis、RabbitMQ 和 Apache Kafka
- 消息传递:
组件之间可以通过向彼此发送消息来进行通信。这可以通过各种通信机制来实现,例如方法调用、函数调用或使用消息队列和通道。组件是解耦的,并且通过消息进行通信,消息可以是同步的或异步的。在分布式环境中,就是一些第三方的mq
使用场景:适用于分布式或并发系统中组件需要交换信息或信号的场景。常用于进程间通信、微服务架构或并行计算。
现实场景:分布式ETL系统
具体使用
为了更好地理解这些概念,思考一下父组件和多个子组件需要共享数据的场景。您可以创建共享上下文或状态类来保存共享数据。父组件中所做的更改会反映在所有子组件中,确保无缝集成和高效的数据通信。但是,更新不是自动的。
typescript
public class SharedContext {
private String sharedData;
public String getSharedData() {
return sharedData;
}
public void setSharedData(String data) {
this.sharedData = data;
}
}
public class ParentComponent {
private SharedContext sharedContext;
public ParentComponent(SharedContext sharedContext) {
this.sharedContext = sharedContext;
}
public void updateSharedData(String newData) {
sharedContext.setSharedData(newData);
}
}
public class ChildComponent {
private SharedContext sharedContext;
public ChildComponent(SharedContext sharedContext) {
this.sharedContext = sharedContext;
}
public void displaySharedData() {
String data = sharedContext.getSharedData();
System.out.println("ChildComponent: " + data);
}
}
public class Main {
public static void main(String[] args) {
SharedContext sharedContext = new SharedContext();
ParentComponent parent = new ParentComponent(sharedContext);
ChildComponent child1 = new ChildComponent(sharedContext);
ChildComponent child2 = new ChildComponent(sharedContext);
parent.updateSharedData("Shared data updated from parent");
child1.displaySharedData(); // Output: ChildComponent: Shared data updated from parent
child2.displaySharedData(); // Output: ChildComponent: Shared data updated from parent
}
}
使用这种方式,对父组件中共享数据所做的更改会反映在所有子组件中,确保它们之间的无缝集成和高效的数据通信,但更新不是自动的。
同时更新子组件
如果想要同时更新子组件,可以使用Java中的发布订阅模式(观察者模式)。该模式可以实现父组件和子组件之间的高效数据通信,并且可以借助观察者模式在 Java 中实现。以下是重写前面示例的方法:
java
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
public class SharedData extends Observable {
private String data;
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
setChanged();
notifyObservers(data);
}
}
public class ParentComponent {
private SharedData sharedData;
public ParentComponent(SharedData sharedData) {
this.sharedData = sharedData;
}
public void updateSharedData(String newData) {
sharedData.setData(newData);
}
}
public class ChildComponent implements Observer {
private String childName;
public ChildComponent(String name, SharedData sharedData) {
this.childName = name;
sharedData.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof SharedData) {
String data = (String) arg;
System.out.println(childName + ": " + data);
}
}
}
public class Main {
public static void main(String[] args) {
SharedData sharedData = new SharedData();
ParentComponent parent = new ParentComponent(sharedData);
ChildComponent child1 = new ChildComponent("ChildComponent 1", sharedData);
ChildComponent child2 = new ChildComponent("ChildComponent 2", sharedData);
parent.updateSharedData("Shared data updated from parent");
// 每个子组件都会更新
}
}
回调
如果我们需要在调用子方法时调用父方法,可以使用回调:
typescript
import java.util.ArrayList;
import java.util.List;
class SharedData {
private String data;
private List<Runnable> childMethodListeners = new ArrayList<>();
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public void addChildMethodListener(Runnable listener) {
childMethodListeners.add(listener);
}
public void removeChildMethodListener(Runnable listener) {
childMethodListeners.remove(listener);
}
public void notifyChildMethodListeners() {
for (Runnable listener : childMethodListeners) {
listener.run();
}
}
}
class ParentComponent {
private SharedData sharedData;
public ParentComponent(SharedData sharedData) {
this.sharedData = sharedData;
}
public void updateSharedData(String newData) {
sharedData.setData(newData);
}
public void onChildMethodCalled() {
System.out.println("ParentComponent: Child method called.");
}
}
class ChildComponent {
private String childName;
private SharedData sharedData;
public ChildComponent(String name, SharedData sharedData) {
this.childName = name;
this.sharedData = sharedData;
}
public void performChildMethod() {
// 加子组件做的事
// 通知父组件
sharedData.notifyChildMethodListeners();
}
public String getChildName() {
return childName;
}
}
public class Main {
public static void main(String[] args) {
SharedData sharedData = new SharedData();
ParentComponent parent = new ParentComponent(sharedData);
sharedData.addChildMethodListener(parent::onChildMethodCalled);
ChildComponent child1 = new ChildComponent("ChildComponent 1", sharedData);
ChildComponent child2 = new ChildComponent("ChildComponent 2", sharedData);
child1.performChildMethod(); // 通知父组件
child2.performChildMethod(); // 通知父组件
}
}
Runnable 接口代表一个可以执行的任务,默认情况下它不接受任何参数或返回值。但是,如果在执行监听器时需要将附加信息或上下文从子组件传递到父组件,您可以使用自定义函数接口或 lambda 表达式。
java
@FunctionalInterface
interface ChildMethodListener {
void onChildMethodCalled(String childName);
}
如果想进一步简化,可以删除共享数据类并将所有内容保留为子状态
typescript
class ChildComponent {
private String childName;
private ParentComponent parent;
public ChildComponent(String name, ParentComponent parent) {
this.childName = name;
this.parent = parent;
}
public void performChildMethod() {
parent.onChildMethodCalled(childName);
}
public String getChildName() {
return childName;
}
}
class ParentComponent {
public void onChildMethodCalled(String childName) {
System.out.println("ParentComponent: Child method called by " + childName);
}
}
public class Main {
public static void main(String[] args) {
ParentComponent parent = new ParentComponent();
ChildComponent child = new ChildComponent("ChildComponent", parent);
child.performChildMethod(); // 通知父组件
System.out.println("Shared data in parent: " + parent.getSharedData());
}
}
如果父级和子级之间有多个中间组件,可以通过每个组件将数据传递到链中的下一个组件来维护数据流
typescript
class ChildComponent {
private String childName;
private MiddleComponent1 middle1;
public ChildComponent(String name, MiddleComponent1 middle1) {
this.childName = name;
this.middle1 = middle1;
}
public void performChildMethod() {
middle1.onChildMethodCalled(childName);
}
public String getChildName() {
return childName;
}
}
class MiddleComponent1 {
private MiddleComponent2 middle2;
public MiddleComponent1(MiddleComponent2 middle2) {
this.middle2 = middle2;
}
public void onChildMethodCalled(String childName) {
middle2.onChildMethodCalled(childName);
}
}
class MiddleComponent2 {
private ParentComponent parent;
public MiddleComponent2(ParentComponent parent) {
this.parent = parent;
}
public void onChildMethodCalled(String childName) {
parent.onChildMethodCalled(childName);
}
}
class ParentComponent {
public void onChildMethodCalled(String childName) {
System.out.println("ParentComponent: Child method called by " + childName);
}
}
public class Main {
public static void main(String[] args) {
ParentComponent parent = new ParentComponent();
MiddleComponent2 middle2 = new MiddleComponent2(parent);
MiddleComponent1 middle1 = new MiddleComponent1(middle2);
ChildComponent child = new ChildComponent("ChildComponent", middle1);
child.performChildMethod();
System.out.println("Shared data in parent: " + parent.getSharedData());
}
}
你能看出这里有什么问题吗?类耦合度很高,因此很难在不同的上下文中使用它们、测试它们等。我们可以引入事件总线作为通信中介来解决此问题。
typescript
import java.util.ArrayList;
import java.util.List;
// 用于发布和订阅事件的事件总线
class EventBus {
private List<EventListener> listeners = new ArrayList<>();
public void subscribe(EventListener listener) {
listeners.add(listener);
}
public void unsubscribe(EventListener listener) {
listeners.remove(listener);
}
public void publish(String message) {
for (EventListener listener : listeners) {
listener.onEvent(message);
}
}
}
class ParentComponent implements EventListener {
private String sharedData;
private MiddleComponent1 middle1;
public MiddleComponent1 getMiddleComponent1(){
return middle1;
}
public ParentComponent() {
this.sharedData = "";
}
public void updateSharedData(String newData) {
sharedData = newData;
}
@Override
public void onEvent(String message) {
System.out.println("ParentComponent: Received message - " + message);
this.sharedData = message;
}
public String getSharedData() {
return sharedData;
}
public void createMiddleComponent() {
MiddleComponent1 middle1 = new MiddleComponent1();
// 订阅父级事件
EventBus eventBus = middle1.getEventBus();
eventBus.subscribe(this);
// 在子组件中执行操作
child.performChildMethod();
System.out.println("Shared data in parent: " + getSharedData());
}
}
class MiddleComponent1 {
private EventBus eventBus;
private MiddleComponent2 middle2;
public MiddleComponent2 getMiddleComponent2(){
return middle2;
}
public MiddleComponent1() {
eventBus = new EventBus();
}
public EventBus getEventBus() {
return eventBus;
}
public void createMiddleComponent() {
MiddleComponent2 middle2 = new MiddleComponent2(eventBus);
}
}
class MiddleComponent2 {
private EventBus eventBus;
private ChildComponent child;
public ChildComponent getChild(){
return child;
}
public MiddleComponent2(EventBus eventBus) {
eventBus = eventBus;
}
public void createChildComponent() {
ChildComponent child = new ChildComponent("ChildComponent", middle2);
}
}
class ChildComponent {
private String childName;
private EventBus eventBus;
public ChildComponent(String name, EventBus eventBus) {
this.childName = name;
this.eventBus = eventBus;
}
public void performChildMethod() {
eventBus.publish("Child method called by " + childName);
}
public String getChildName() {
return childName;
}
}
interface EventListener {
void onEvent(String message);
}
public class Main {
public static void main(String[] args) {
ParentComponent parent = new ParentComponent();
parent.createMiddleComponents();
parent.getMiddleComponent1().getMiddleComponent2().getChild().performChildMethod();
System.out.println("Updated shared data in parent: " + getSharedData());
}
}
总结
在父组件/类之间实现轻松的数据转换对于维护精简且互连的系统至关重要。每种策略都有其优点和缺点,因此我们在选择之前需要深入研究每种策略。通过这些策略,在软件开发中协调亲子关系的旅程变成了对效率、简单性和有效数据流的追求。