You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Vaadin两个版本的Grid实现可复用的同步扩展

Solution: Adapter + Delegation Pattern for Shared Sync Logic

Your goal of reusing synchronization logic across two incompatible Vaadin Grid versions is a classic case where adapter + delegation shines—this avoids the pitfalls of your three previous attempts while keeping your core logic DRY (Don’t Repeat Yourself).

Here's a step-by-step breakdown of the architecture:

1. Core Sync Library (SG)

First, extract all version-agnostic synchronization logic into a standalone core library. This library has no dependencies on any Vaadin version.

Key Components in SG:

  • SyncGridAdapter<T>: An interface that defines the "contract" between your core sync logic and the Vaadin Grid implementations. It abstracts away all Vaadin-specific operations the core needs:

    public interface SyncGridAdapter<T extends Selectable> {
        // Access the UI thread safely
        void accessUI(Runnable task);
        // Refresh all items in the grid
        void refreshAllItems();
        // Register a listener for item double-clicks
        void onItemDoubleClick(Consumer<T> handler);
        // Get the class type of items in the grid
        Class<T> getItemClass();
        // Get the static list of values for this item type
        List<T> getStaticItemList();
    }
    
  • SynchronizationManager<T>: The heart of your sync logic. It handles observer registration, cross-grid notifications, and data state management—all using the adapter to interact with the underlying Grid:

    public class SynchronizationManager<T extends Selectable> {
        private final SyncGridAdapter<T> adapter;
    
        public SynchronizationManager(SyncGridAdapter<T> adapter) {
            this.adapter = adapter;
            // Initialize data provider filters via the adapter
            setupDataProviderFilter();
            // Register double-click handler via the adapter
            adapter.onItemDoubleClick(this::handleItemSelection);
        }
    
        private void setupDataProviderFilter() {
            // Your filter logic here (uses adapter to get static list)
            // Example: dataProvider.addFilter(bean -> !bean.isSelected());
        }
    
        public void registerAsObserver() {
            ObserverStore.getInstance().get(adapter.getItemClass()).add(adapter);
        }
    
        public void unregisterAsObserver() {
            ObserverStore.getInstance().get(adapter.getItemClass()).remove(adapter);
        }
    
        private void handleItemSelection(T item) {
            item.setSelected(true);
            // Notify all observers to refresh
            ObserverStore.getInstance().get(adapter.getItemClass())
                .forEach(obs -> obs.accessUI(() -> obs.refreshAllItems()));
        }
    
        public void reload() {
            adapter.accessUI(adapter::refreshAllItems);
        }
    }
    

2. SG8 Library (Vaadin 8 Implementation)

Your SynchronizedGrid8 inherits Vaadin 8's Grid<T> and implements the SyncGridAdapter<T> interface. It delegates all sync-related work to the SynchronizationManager:

public class SynchronizedGrid8<T extends Selectable> extends Grid<T> implements SyncGridAdapter<T> {
    private final SynchronizationManager<T> syncManager;
    private final Class<T> itemClass;

    public SynchronizedGrid8(Class<T> itemClass) {
        super(itemClass);
        this.itemClass = itemClass;
        // Initialize core sync logic with this adapter
        this.syncManager = new SynchronizationManager<>(this);
        
        // Vaadin 8-specific setup (data provider)
        ListDataProvider<T> dataProvider = new ListDataProvider(getStaticItemList());
        setDataProvider(dataProvider);
        dataProvider.addFilter(bean -> !bean.isSelected());
    }

    @Override
    public void attach() {
        super.attach();
        syncManager.registerAsObserver();
    }

    @Override
    public void detach() {
        super.detach();
        syncManager.unregisterAsObserver();
    }

    // Implement SyncGridAdapter methods for Vaadin 8
    @Override
    public void accessUI(Runnable task) {
        getUI().access(task);
    }

    @Override
    public void refreshAllItems() {
        getDataProvider().refreshAll();
    }

    @Override
    public void onItemDoubleClick(Consumer<T> handler) {
        addItemClickListener(clickEvent -> {
            if (clickEvent.getMouseEventDetails().isDoubleClick()) {
                handler.accept(clickEvent.getItem());
            }
        });
    }

    @Override
    public Class<T> getItemClass() {
        return itemClass;
    }

    @Override
    public List<T> getStaticItemList() {
        return StaticListStore.getInstance().get(itemClass);
    }
}

3. SG10 Library (Vaadin Flow Implementation)

Similarly, SynchronizedGrid10 inherits Vaadin Flow's Grid<T> and implements SyncGridAdapter<T>. The only differences are the Vaadin-specific API calls—all core sync logic stays in the SynchronizationManager:

public class SynchronizedGrid10<T extends Selectable> extends Grid<T> implements SyncGridAdapter<T> {
    private final SynchronizationManager<T> syncManager;
    private final Class<T> itemClass;

    public SynchronizedGrid10(Class<T> itemClass) {
        super(itemClass);
        this.itemClass = itemClass;
        this.syncManager = new SynchronizationManager<>(this);
        
        // Vaadin Flow-specific data provider setup
        ListDataProvider<T> dataProvider = new ListDataProvider<>(getStaticItemList());
        setDataProvider(dataProvider);
        dataProvider.addFilter(bean -> !bean.isSelected());
    }

    @Override
    public void onAttach(AttachEvent event) {
        super.onAttach(event);
        syncManager.registerAsObserver();
    }

    @Override
    public void onDetach(DetachEvent event) {
        super.onDetach(event);
        syncManager.unregisterAsObserver();
    }

    // Implement SyncGridAdapter methods for Vaadin Flow
    @Override
    public void accessUI(Runnable task) {
        getUI().ifPresent(ui -> ui.access(task));
    }

    @Override
    public void refreshAllItems() {
        getDataProvider().refreshAll();
    }

    @Override
    public void onItemDoubleClick(Consumer<T> handler) {
        // Vaadin Flow uses a dedicated double-click listener
        addItemDoubleClickListener(event -> handler.accept(event.getItem()));
    }

    @Override
    public Class<T> getItemClass() {
        return itemClass;
    }

    @Override
    public List<T> getStaticItemList() {
        return StaticListStore.getInstance().get(itemClass);
    }
}

Why This Works Better Than Your Previous Attempts:

  • No multiple inheritance: We use interface implementation + delegation instead, which is fully Java-compliant.
  • Clean separation of concerns: Core sync logic lives entirely in SG, while SG8/SG10 only handle Vaadin-specific adaptations (no business logic here).
  • Minimal boilerplate: The adapter interface standardizes the operations the core needs, so you only write Vaadin-specific mappings once per version.
  • Scalable: If you add a Vaadin 24 version later, you just create a new SynchronizedGrid24 class that implements SyncGridAdapter—no changes needed to the core SG library.

内容的提问来源于stack exchange,提问作者keanni

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 20:12:35