2012-03-23 28 views

Trả lời

13

Tôi không biết về bất kỳ tiêu chuẩn hoặc bên thứ 3, nhưng nó rất dễ dàng, chỉ cần tạo một lớp bao bọc khác Bản đồ và thực hiện các giao diện bản đồ:

public class MapListener<K, V> implements Map<K, V> { 

    private final Map<K, V> delegatee; 

    public MapListener(Map<K, V> delegatee) { 
     this.delegatee = delegatee; 
    } 

    // implement all Map methods, with callbacks you need. 

} 
+0

Thay vì 'thực hiện Bản đồ ', tôi nghĩ rằng việc sử dụng 'kéo dài Bản đồ Trừu tượng ' dễ dàng hơn. (Nhưng tôi đồng ý về cấu trúc nó như là một wrapper mà đại biểu đến bản đồ khác.) – ruakh

+3

Bản đồ chuyển tiếp của ổi về cơ bản chính xác điều này - nó chuyển tiếp tất cả các phương pháp cho các đại biểu, và sau đó bạn có thể ghi đè lên để hương vị. –

+0

Đó là những gì tôi nghĩ, chỉ cần câu cá cho mã tái sử dụng trước khi viết của riêng tôi :) –

4

Phần thưởng thức. Đây là đại diện, không phải là tiêu chuẩn. Tất nhiên nó có vấn đề.

public class ListenerMap extends HashMap { 

    public static final String PROP_PUT = "put"; 
    private PropertyChangeSupport propertySupport; 

    public ListenerMap() { 
     super(); 
     propertySupport = new PropertyChangeSupport(this); 
    } 

    public String getSampleProperty() { 
     return sampleProperty; 
    } 

    @Override 
    public Object put(Object k, Object v) { 
     Object old = super.put(k, v); 
     propertySupport.firePropertyChange(PROP_PUT, old, v); 
     return old; 
    } 

     public void addPropertyChangeListener(PropertyChangeListener listener) { 
     propertySupport.addPropertyChangeListener(listener); 
    } 

    public void removePropertyChangeListener(PropertyChangeListener listener) { 
     propertySupport.removePropertyChangeListener(listener); 
    } 
} 
+0

Mở rộng không phải là điều tốt cho yêu cầu này, tôi tin rằng việc tạo một trình bao bọc cho mọi bản đồ là tốt hơn nhiều. –

+0

@AmirPashazadeh ý bạn là gì bởi "trình bao bọc cho mọi bản đồ"? –

+0

Trình bao bọc chấp nhận bất kỳ triển khai bản đồ nào làm đối số, do đó hành vi mặc định của bản đồ vẫn như cũ. –

0

gì bạn là chủ yếu hỏi cho là một Cache có thể cung cấp thông báo sự kiện. Có một số sản phẩm ra có như Infinispan đã cung cấp cho bạn nhưng không biết trường hợp sử dụng của bạn khó để khuyên bạn nên.

Nếu bạn muốn có một ObservableMap đơn giản, nó sẽ dễ thực hiện. Bạn chỉ cần tạo một mẫu Observer. Bạn có thể tìm thấy một ví dụ here.

+0

Infinispan sẽ là tuyệt vời, nhưng tôi cần điều này bên trong một applet, vì vậy tôi có thể cung cấp dữ liệu đến .... infinispan! Chỉ vì các cuộc gọi javascript được coi là mã chưa ký tôi không thể gửi trực tiếp: ( –

+0

Infinispan có cả chế độ nhúng và phân phối. Bạn có thể nhúng Infinispan vào JVM của mình. – uaarkoti

0

Đây là ví dụ hoạt động của bản đồ kích hoạt sự kiện thay đổi thuộc tính khi đặt và xóa. Việc thực hiện được chia thành hai lớp:

ListenerModel

Chứa các phương pháp liên quan đến việc thêm và loại bỏ các thính giả thay đổi và cũng là một phương pháp để bắn những thay đổi bất động sản.

ListenerMap

Mở rộng ListenerModel và implementes giao diện java.util.Map bởi đoàn. Nó chỉ kích hoạt thay đổi thuộc tính trong phương thức put và remove. Sẽ có ý nghĩa khi kích hoạt các thuộc tính trên các phương thức khác, ví dụ: clear(), putAll().

ListenerModel

import java.beans.PropertyChangeListener; 
import java.beans.PropertyChangeSupport; 

public class ListenerModel { 

    private final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this); 

    public void addPropertyChangeListener(PropertyChangeListener listener) { 
     changeSupport.addPropertyChangeListener(listener); 
    } 

    public void removePropertyChangeListener(PropertyChangeListener listener) { 
     changeSupport.removePropertyChangeListener(listener); 
    } 

    protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { 
     changeSupport.firePropertyChange(propertyName, oldValue, newValue); 
    } 
} 

ListenerMap

import java.util.*; 

public class ListenerMap<K, V> extends ListenerModel implements Map<K, V> { 

    public static final String PROP_PUT = "put"; 

    public static final String REMOVE_PUT = "remove"; 

    private Map<K, V> delegate = new LinkedHashMap<>(); 

    @Override 
    public void clear() { 
     delegate.clear(); 
    } 

    @Override 
    public boolean containsKey(Object key) { 
     return delegate.containsKey(key); 
    } 

    @Override 
    public boolean containsValue(Object value) { 
     return delegate.containsValue(value); 
    } 

    @Override 
    public Set<Entry<K, V>> entrySet() { 
     return delegate.entrySet(); 
    } 

    @Override 
    public V get(Object key) { 
     return delegate.get(key); 
    } 

    @Override 
    public boolean isEmpty() { 
     return delegate.isEmpty(); 
    } 

    @Override 
    public Set<K> keySet() { 
     return delegate.keySet(); 
    } 

    @Override 
    public V put(K key, V value) { 
     V oldValue = delegate.put(key, value); 
     firePropertyChange(PROP_PUT, oldValue == null ? null : new AbstractMap.SimpleEntry<>(key, oldValue), 
       new AbstractMap.SimpleEntry<>(key, value)); 
     return oldValue; 
    } 

    @Override 
    public void putAll(Map<? extends K, ? extends V> m) { 
     delegate.putAll(m); 
    } 

    @Override 
    public V remove(Object key) { 
     V oldValue = delegate.remove(key); 
     firePropertyChange(REMOVE_PUT, oldValue == null ? null : new AbstractMap.SimpleEntry<>(key, oldValue), 
       null); 
     return oldValue; 
    } 

    @Override 
    public int size() { 
     return delegate.size(); 
    } 

    @Override 
    public Collection<V> values() { 
     return delegate.values(); 
    } 
} 

Dưới đây là một thử nghiệm JUnit 4:

import org.junit.Before; 
import org.junit.Test; 

import java.beans.PropertyChangeListener; 
import java.util.Map; 

import static org.hamcrest.core.Is.is; 
import static org.hamcrest.core.IsNull.nullValue; 
import static org.junit.Assert.assertThat; 

/** 
* Created by Gil on 01/07/2017. 
*/ 
public class ListenerMapTest { 

    private ListenerMap<String, String> map; 

    @Before 
    public void setUp() throws Exception { 
     map = new ListenerMap<>(); 
    } 

    @Test 
    public void whenPut_ShouldFireTrigger() throws Exception { 
     boolean[] fired = {false}; 
     Map.Entry<String, String>[] listenEntry = new Map.Entry[1]; 
     boolean[] checkNull = {true}; 
     PropertyChangeListener propertyChangeListener = evt -> { 
      if (ListenerMap.PROP_PUT.equals(evt.getPropertyName())) { 
       if(checkNull[0]) { 
        assertThat(evt.getOldValue(), is(nullValue())); 
       } 
       else { 
        Map.Entry<String, String> oldValue = (Map.Entry<String, String>) evt.getOldValue(); 
        assertThat(oldValue.getKey(), is("k1")); 
        assertThat(oldValue.getValue(), is("v1")); 
       } 
       listenEntry[0] = (Map.Entry<String, String>) evt.getNewValue(); 
       fired[0] = true; 
      } 
     }; 
     map.addPropertyChangeListener(propertyChangeListener); 
     map.put("k1", "v1"); 
     assertThat(fired[0], is(true)); 
     assertThat(listenEntry[0].getKey(), is("k1")); 
     assertThat(listenEntry[0].getValue(), is("v1")); 
     checkNull[0] = false; 
     map.put("k1", "v2"); 
    } 

    @Test 
    public void whenRemove_ShouldNotFire() throws Exception { 
     boolean[] fired = {false}; 
     PropertyChangeListener propertyChangeListener = evt -> { 
      fired[0] = true; 
     }; 
     map.addPropertyChangeListener(propertyChangeListener); 
     map.put("k1", "v1"); 
     assertThat(fired[0], is(true)); 
     fired[0] = false; 
     map.removePropertyChangeListener(propertyChangeListener); 
     map.put("k2", "v2"); 
     assertThat(fired[0], is(false)); 
    } 

} 
Các vấn đề liên quan