如何记录现有代码的List接口方法

时间:2016-12-20 10:27:17

标签: java

我有现有的代码库,有时使用ArrayList或LinkedList,我需要找到一种方法来记录添加或删除的时间,以跟踪已添加或删除的内容。

确保登录到位的最佳方法是什么?

例如。

ArrayList<Integer> list = new ArrayList<Integer>();
list.add(123);

LinkedList<Integer> anotherNewList = new LinkedList<Integer>();
anotherNewList.add(333);

不确定我是否可以拦截add方法来实现此目的,或者创建实现java.util.List接口的覆盖类,然后使用它。无论哪种方式,我都在寻找一个需要最少干预的优秀解决方案,而且不需要使用任何第三方包装......

7 个答案:

答案 0 :(得分:3)

我会使用所谓的Decorator Pattern来包装你的列表。

这只是一个简单的示例代码,只是为了给你一个想法:

private static class LogDecorator<T> implements Collection<T> {
    private final Collection<T> delegate;

    private LogDecorator(Collection<T> delegate) {this.delegate = delegate;}

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

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

    @Override
    public boolean contains(Object o) {
      return delegate.contains(o);
    }

    @Override
    public Iterator<T> iterator() {
      return delegate.iterator();
    }

    @Override
    public Object[] toArray() {
      return delegate.toArray();
    }

    @Override
    public <T1> T1[] toArray(T1[] a) {
      return delegate.toArray(a);
    }

    @Override
    public boolean add(T t) {
      // ADD YOUR INTERCEPTING CODE HERE

      return delegate.add(t);
    }

    @Override
    public boolean remove(Object o) {
      return delegate.remove(o);
    }

    @Override
    public boolean containsAll(Collection<?> c) {
      return delegate.containsAll(c);
    }

    @Override
    public boolean addAll(Collection<? extends T> c) {
      return delegate.addAll(c);
    }

    @Override
    public boolean removeAll(Collection<?> c) {
      return delegate.removeAll(c);
    }

    @Override
    public boolean retainAll(Collection<?> c) {
      return delegate.retainAll(c);
    }

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

答案 1 :(得分:2)

实际上并没有一种简单的方法。

这些类是&#34;标准库的一部分&#34 ;;所以你不能改变他们的行为。您可以创建自己的版本;并使用类路径排序来使用它们;但这真的很脏。

唯一的另一个选择:扩展这些类; @Override您要记录的方法;并确保所有您的来源使用您自己的这些类的版本。或者,如果您更喜欢组合而不是继承,那么您可以选择装饰模式;正如JDC的回答所建议的那样。

&#34;第三&#34;选项实际上是不同的 - 你转向面向方面的编程(例如使用AspectJ)并使用这些工具来操作字节码级别的东西。但这增加了一层全新的“复杂性”。对你的产品;因此我不认为它是真正的选择。

编辑您的回答:您似乎不了解界面与实现之间的区别?!界面简单地描述了一组方法签名;但是为了在这些方法背后有真正的代码,需要有一个实现类。你看,当你这样做时

List<X> things = new ArrayList<>();

things真实类型是ArrayList;但你很少关心真正的类型;知道你可以在things上使用所有那些List方法就足够了。因此,当您创建List接口的一些新实现时......并不会影响任何现有的

... = new ArrayList ...

声明。您必须将所有分配更改为

List<X> things = new YourNewListImplementation<>();

答案 2 :(得分:2)

JDC提供了一个很好的方法 我想带来重要的精确性 装饰器模式允许通过向实例添加或移除新的责任来创建一个装饰另一个类的类 在您的情况下,您想要增加责任。

装饰器不是一种侵入式模式,但装饰器类必须符合它所装饰的类 因此,在您的情况下,具有派生自Collection接口的装饰器不符合装饰对象,因为List具有Collection没有的方法。
 您需要装饰List个实例,因此装饰器应来自List类型。

此外,装饰器类可以根据需要在它装饰的类的操作之前和之后进行处理,但是它也负责调用装饰类的原始操作。
在您的情况下,您想知道是否在List中添加或删除了元素。要实现它,因为方法结果会对您是否记录信息产生影响,所以最好先将处理委托给装饰对象,然后装饰者可以执行其处理。
有时候,你不需要装饰方法,不要做,但不要忘记委托装饰对象。

import java.util.Iterator;
import java.util.List;

public class DecoratorList<T> implements List<T> {

    private static final Tracer tracer = ....;
    private List<T> decorated;

    private DecoratorList(List<T> decorated) {
      this.decorated=decorated;
    }

    // no decorated methods
            ....
    @Override
    public int size() {
      return this.decorated.size();
    }

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

    @Override
    public boolean contains(Object o) {
      return this.decorated.contains(o);
    }

    @Override
    public Iterator<T> iterator() {
      return this.decorated.iterator();
    }
           ....
    // end no decorated methods


    // exemple of decorated methods
    @Override
    public void add(int index, T element) {
      tracer.info("element " + element + " added to index " + index);
      this.decorated.add(index,element);
    }

    @Override
    public boolean remove(Object o) {
     final boolean isRemoved = this.decorated.remove(o);
      if (isRemoved){
        tracer.info("element " +  o + " removed");
      }
      return isRemoved;
    }

}

如上所述,装饰者不会对装饰物体造成侵扰 所以这个想法不是改变你的代码,而是在实例化列表之后添加装饰操作 如果您在声明列表变量时没有按界面编程,那就是声明ArrayList list = new ArrayList()而不是List list = new ArrayList(),当然您应该将声明的类型更改为List但它不会相反,打破代码。

以下是您的示例代码:

ArrayList<Integer> list = new ArrayList<Integer>();
list.add(123);

LinkedList<Integer> anotherNewList = new LinkedList<Integer>();
anotherNewList.add(333);

现在,你可以这样做:

List<Integer> list = new ArrayList<Integer>();
list = new DecoratorList<Integer>(list); // line added
list.add(123);

List<Integer> anotherNewList = new LinkedList<Integer>();
anotherNewList = new DecoratorList<Integer>(anotherNewList); // line added
anotherNewList.add(333);

为了简化任务并使其更安全,您甚至可以创建一个util方法来应用列表中的装饰:

private static <T> List<T> decorateList(List<T> list) {
  list = new DecoratorList<T>(list); 
  return list;
}

并称之为:

List<Integer> list = new ArrayList<Integer>();
list = decorateList(list); // line added
list.add(123);

答案 3 :(得分:0)

您的列表是此处的来源。您需要跟踪源的更改。这是Observer模式的一个很好的自然例子。您可以创建一个Observable作为您的列表。然后创建一些Observers并将它们注册到Observable。更改Observable后,通知所有已注册的观察员。在Observer中,您可以使用输入事件记录更改。你应该在这里实际上实现一些ObservableCollection。您可以使用Java Rx完成此工作。请查看下面给出的示例代码。

package com.test;

import java.util.ArrayList;
import java.util.List;

import rx.Observable;
import rx.subjects.PublishSubject;

public class ObservableListDemo {

    public static class ObservableList<T> {

        protected final List<T> list;
        protected final PublishSubject<T> onAdd;

        public ObservableList() {
            this.list = new ArrayList<T>();
            this.onAdd = PublishSubject.create();
        }

        public void add(T value) {
            list.add(value);
            onAdd.onNext(value);
        }

        public Observable<T> getObservable() {
            return onAdd;
        }
    }

    public static void main(String[] args) throws InterruptedException {
         ObservableList<Integer> observableList = new ObservableList<>();

            observableList.getObservable().subscribe(System.out::println);

            observableList.add(1);
            Thread.sleep(1000);
            observableList.add(2);
            Thread.sleep(1000);
            observableList.add(3);
    }

}

希望这会有所帮助。快乐的编码!

答案 4 :(得分:0)

您可以使用Aspects - 但它会记录每个remove@Aspect public class ListLoggerAspect { @Around("execution(* java.util.List.add(..))") public boolean aroundAdd(ProceedingJoinPoint joinPoint) throws Throwable { boolean result = (boolean) joinPoint.proceed(joinPoint.getArgs()); // do the logging return result; } } 来电:

<aspectj>
    <aspects>
        <aspect name="com.example.ListLoggerAspect"/>
    </aspects>
</aspectj>

您需要在META-INF / aop.xml中配置方面:

select * from entity  
order by (city_id <=> 17782 OR province_id <=> 654 OR country_id <=> 12) DESC, 
         city_id <=> 17782 DESC, province_id <=> 654 DESC, country_id <=> 12 DESC;

答案 5 :(得分:0)

实现此目的的一种简单方法是将源列表包装在ObservableList中,并将其用作基本列表。您只需在此列表中添加一个侦听器即可捕获每个修改(如果您愿意,还可以注销)

示例:

List obs = FXCollections.observableList(myOriginalList);
obs.addListener(c -> {
    for(Item it : c.getRemoved())
        System.out.println(it);

    for(Item it : c.getAddedSubList())
        System.out.println(it);
});

有关如何添加好听众的信息,请参阅the javafx documentation

答案 6 :(得分:0)

我们需要更多信息才能找到合适的解决方案。但我看到了很多选择。

  1. 您可以使用装饰器跟踪更改。
  2. 您可以复制该集合并计算更改
  3. 您可以使用aspect来“装饰”JVM中的每个List
  4. 更改现有代码库(一点点)
  5. 1)如果您确切知道列表的使用方式,则可以正常工作,一旦返回新代码,您就是唯一的用户。所以现有代码不能有任何添加到原始列表的方法(因为会调用委托上的添加/删除而不是装饰集合。)

    2)当多个类可以修改列表时使用此方法。在开始任何修改之前,您需要能够获得列表的副本,然后计算之后发生的事情。如果您可以访问Apache Collections库,则可以使用CollectionUtils来计算交集和分离。

    3)此解决方案需要一些编织(编译或加载时间),因为这将为每个List创建一个代理,因此它可以在方法调用周围添加回调代码。我不推荐这个选项,除非你对方面的工作方式有很好的理解,因为这个解决方案有一个相当陡峭的学习曲线,如果出现问题并且你需要调试代码,它可能有点棘手。

    4)你说现有的代码库让我相信,如果你真的想要,你可以真正改变代码。如果这是可能的,那就是我选择的方法。如果List的用户需要能够跟踪更改,那么最好的解决方案是库返回ChangeTrackingList(定义跟踪方法的接口),您可以使用装饰来构建。

    装饰时你必须注意的一点是,List有一个removeAll()和一个addAll(),这些方法可能会也可能不会调用add()和remove(),这取决于列表的实现。如果你不知道如何在内部调用这些方法,你最终可能会看到一个对象被删除两次(除非你可以使用一组)。