Có thư viện Java nào có thể "khác biệt" hai Đối tượng không?


86

Có thư viện tiện ích Java nào tương tự với khác biệt của chương trình Unix, nhưng dành cho Đối tượng không? Tôi đang tìm thứ gì đó có thể so sánh hai đối tượng cùng loại và tạo cấu trúc dữ liệu đại diện cho sự khác biệt giữa chúng (và có thể so sánh đệ quy sự khác biệt trong các biến phiên bản). Tôi không tìm cách triển khai Java của một khác biệt văn bản. Tôi cũng không tìm kiếm trợ giúp về cách sử dụng phản chiếu để thực hiện việc này.

Ứng dụng mà tôi đang duy trì có một bản triển khai chức năng này còn mỏng manh, có một số lựa chọn thiết kế kém và cần được viết lại, nhưng sẽ tốt hơn nữa nếu chúng ta có thể sử dụng một thứ gì đó đã có sẵn.

Đây là một ví dụ về loại thứ tôi đang tìm kiếm:

SomeClass a = new SomeClass();
SomeClass b = new SomeClass();

a.setProp1("A");
a.setProp2("X");

b.setProp1("B");
b.setProp2("X");

DiffDataStructure diff = OffTheShelfUtility.diff(a, b);  // magical recursive comparison happens here

Sau khi so sánh, tiện ích sẽ cho tôi biết rằng "prop1" là khác nhau giữa hai đối tượng và "prop2" cũng vậy. Tôi nghĩ DiffDataStructure là một cái cây là điều tự nhiên nhất, nhưng tôi sẽ không kén chọn nếu mã đáng tin cậy.


8
kiểm tra cái này, code.google.com/p/jettison
delk


13
Đây không phải là bản sao của "Cách kiểm tra sự bằng nhau của đồ thị đối tượng phức tạp?" Tôi đang tìm kiếm một thư viện, không phải lời khuyên về cách làm điều đó. Hơn nữa, tôi quan tâm đến vùng đồng bằng, không phải chúng bằng nhau hay không.
Kaypro II

1
Hãy xem github.com/jdereg/java-util có tiện ích GraphComparator. Lớp này sẽ tạo ra một Danh sách các đồ thị delta từ giữa đến đối tượng. Ngoài ra, nó có thể hợp nhất (áp dụng) một đồng bằng vào một đồ thị. Hiệu quả thì nó là List <Deltosystem = GraphComparator (rootA, rootB). Cũng như GraphComparator.applyDelta (rootB, List <Deltosystem).
John DeRegnaucourt

Tôi biết bạn không muốn sử dụng phản chiếu, nhưng đó chắc chắn là / Cách đơn giản nhất đơn giản để thực hiện một cái gì đó như thế này
RobOhRob

Câu trả lời:


42

Có thể hơi muộn, nhưng tôi cũng ở trong hoàn cảnh giống bạn và cuối cùng đã tạo thư viện của riêng mình cho chính xác trường hợp sử dụng của bạn. Vì tôi buộc phải tự mình đưa ra giải pháp nên tôi quyết định phát hành nó trên Github, để người khác đỡ vất vả hơn. Bạn có thể tìm thấy nó ở đây: https://github.com/SQiShER/java-object-diff

--- Chỉnh sửa ---

Dưới đây là một ví dụ sử dụng nhỏ dựa trên mã OP:

SomeClass a = new SomeClass();
SomeClass b = new SomeClass();

a.setProp1("A");
a.setProp2("X");

b.setProp1("B");
b.setProp2("X");

DiffNode diff = ObjectDifferBuilder.buildDefault().compare(a, b);

assert diff.hasChanges();
assert diff.childCount() == 1;
assert diff.getChild('prop1').getState() == DiffNode.State.CHANGED;

1
Tôi có thể chỉ cho bạn bài đăng này hiển thị một trường hợp sử dụng của java-object-diff không? stackoverflow.com/questions/12104969/… Cảm ơn rất nhiều vì thư viện hữu ích này!
Matthias Wuttke

Tôi đã có một cái nhìn tại bài viết của bạn một viết một câu trả lời mà bật ra được cách quá lâu để gửi như một bình luận, vì vậy đây là "ý chính" của nó: gist.github.com/3555555
SQiShER

2
Khéo léo. Tôi cũng đã viết phần triển khai của riêng mình. Tôi không chắc mình sẽ có cơ hội thực sự khám phá cách triển khai của bạn, nhưng tôi tò mò về cách bạn xử lý Danh sách. Tôi đã kết thúc việc xử lý tất cả các khác biệt trong bộ sưu tập dưới dạng khác biệt của Maps (bằng cách xác định các khóa theo những cách khác nhau tùy thuộc vào việc đó là danh sách, tập hợp hay bản đồ; sau đó sử dụng các thao tác đặt trên keySet). Tuy nhiên, phương pháp đó quá nhạy cảm với các thay đổi chỉ mục danh sách. Tôi đã không giải quyết vấn đề đó vì tôi không cần ứng dụng của mình, nhưng tôi tò mò về cách bạn xử lý nó (giống như một text-diff, tôi cho là vậy).
Kaypro II

2
Bạn đưa lên một điểm tốt. Các bộ sưu tập thực sự khó đối phó. Đặc biệt là nếu bạn ủng hộ việc hợp nhất, giống như tôi. Tôi đã giải quyết vấn đề bằng cách sử dụng danh tính đối tượng để phát hiện xem một mục đã được thêm, thay đổi hoặc xóa (thông qua Mã băm, bằng và chứa). Điều tốt về điều đó là tôi có thể coi tất cả các bộ sưu tập như nhau. Điều tồi tệ là tôi không thể xử lý đúng cách (ví dụ) ArrayLists có chứa cùng một đối tượng nhiều lần. Tôi muốn thêm hỗ trợ cho điều đó, nhưng nó có vẻ khá phức tạp và may mắn thay, chưa ai yêu cầu điều đó. :-)
SQiShER

Daniel, cảm ơn vì bình luận của bạn! Bạn nói đúng, việc tạo lại đối tượng ban đầu từ các khác biệt là rất khó, nhưng điều này trong trường hợp của tôi không quá quan trọng. Ban đầu, tôi đã lưu trữ các phiên bản trước của đối tượng của mình trong cơ sở dữ liệu - như bạn đề xuất. Vấn đề là hầu hết các phần của các tài liệu lớn không thay đổi, có rất nhiều dữ liệu bị trùng lặp. Đây là lý do tại sao tôi bắt đầu tìm kiếm các lựa chọn thay thế và tôi tìm thấy java-object-diff. Tôi cũng gặp khó khăn với bộ sưu tập / bản đồ và đã thay đổi phương thức "bằng" của mình để kiểm tra tính bình đẳng của cơ sở dữ liệu chính (chứ không phải toàn bộ đối tượng), điều này đã giúp ích.
Matthias Wuttke

39

http://javers.org là thư viện thực hiện chính xác những gì bạn cần: có các phương thức như so sánh (Object leftGraph, Object rightGraph) trả về đối tượng Diff. Diff chứa danh sách các thay đổi (ReferenceChange, ValueChange, PropertyChange), ví dụ:

given:
DummyUser user =  dummyUser("id").withSex(FEMALE).build();
DummyUser user2 = dummyUser("id").withSex(MALE).build();
Javers javers = JaversTestBuilder.newInstance()

when:
Diff diff = javers.compare(user, user2)

then:
diff.changes.size() == 1
ValueChange change = diff.changes[0]
change.leftValue == FEMALE
change.rightValue == MALE

Nó có thể xử lý các chu trình trong đồ thị.

Ngoài ra, bạn có thể nhận Ảnh chụp nhanh của bất kỳ đối tượng đồ thị nào. Javers có trình tuần tự JSON và trình giải mã để chụp nhanh và thay đổi để bạn có thể dễ dàng lưu chúng vào cơ sở dữ liệu. Với thư viện này, bạn có thể dễ dàng triển khai một mô-đun để kiểm tra.


1
điều này không hoạt động. làm thế nào để bạn tạo một phiên bản Javers?
Ryan Vettese

2
Javers có tài liệu tuyệt vời để giải thích tất cả mọi thứ: javers.org
Paweł Szymczyk

2
Tôi đã cố gắng sử dụng nó nhưng chỉ dành cho Java> = 7. Các bạn ơi, vẫn có người làm dự án trên Java 6 !!!
jesantana

2
Điều gì sẽ xảy ra nếu hai đối tượng mà tôi so sánh có một danh sách các đối tượng bên trong và tôi cũng sẽ thấy những khác biệt đó? Mức độ phân cấp mà javers có thể so sánh là gì?
Deepak,

1
Có, bạn sẽ thấy sự khác biệt trên các đối tượng bên trong. Hiện tại, không có ngưỡng cho cấp độ phân cấp. Javers sẽ đi sâu nhất có thể, hạn chế là kích thước ngăn xếp.
Paweł Szymczyk

15

Có, thư viện java- use có một lớp GraphComparator sẽ so sánh hai Đồ thị đối tượng Java. Nó trả về sự khác biệt dưới dạng Danh sách các delta. GraphComparator cũng cho phép bạn hợp nhất (áp dụng) các delta. Mã này chỉ có phụ thuộc vào JDK, không có thư viện nào khác.


12
Dường như một thư viện tốt đẹp, bạn nên đề cập đến rằng bạn là một đóng góp
Christos

3

Tất cả thư viện Javers chỉ hỗ trợ cho Java 7, tôi đã rơi vào tình huống muốn cái này được sử dụng cho dự án Java 6 nên tôi đã tình cờ lấy nguồn và thay đổi cách nó hoạt động cho Java 6. Dưới đây là mã github .

https://github.com/sand3sh/javers-forJava6

Liên kết Jar: https://github.com/sand3sh/javers-forJava6/blob/master/build/javers-forjava6.jar

Tôi chỉ thay đổi các chuyển đổi cast vốn có của Java 7 được hỗ trợ '<>' thành hỗ trợ Java 6. Tôi không đảm bảo rằng tất cả các chức năng sẽ hoạt động vì tôi đã nhận xét một số mã không cần thiết đối với tôi, nó hoạt động cho tất cả các đối tượng tùy chỉnh so sánh.



2

Bạn cũng có thể xem giải pháp từ Apache. Hầu hết các dự án đã có nó trên classpath của họ kể từ một phần của nó là commons-lang.

Kiểm tra sự khác biệt cho (các) trường cụ thể:
http://commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/apache/commons/lang3/builder/DiffBuilder.html

Kiểm tra sự khác biệt bằng cách sử dụng phản ánh:
http://commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/apache/commons/lang3/builder/ReflectionDiffBuilder.html


1

Có thể điều này sẽ hữu ích, tùy thuộc vào nơi bạn sử dụng mã này, nó có thể hữu ích hoặc có vấn đề. Đã kiểm tra mã này.

    /**
 * @param firstInstance
 * @param secondInstance
 */
protected static void findMatchingValues(SomeClass firstInstance,
        SomeClass secondInstance) {
    try {
        Class firstClass = firstInstance.getClass();
        Method[] firstClassMethodsArr = firstClass.getMethods();

        Class secondClass = firstInstance.getClass();
        Method[] secondClassMethodsArr = secondClass.getMethods();


        for (int i = 0; i < firstClassMethodsArr.length; i++) {
            Method firstClassMethod = firstClassMethodsArr[i];
            // target getter methods.
            if(firstClassMethod.getName().startsWith("get") 
                    && ((firstClassMethod.getParameterTypes()).length == 0)
                    && (!(firstClassMethod.getName().equals("getClass")))
            ){

                Object firstValue;
                    firstValue = firstClassMethod.invoke(firstInstance, null);

                logger.info(" Value "+firstValue+" Method "+firstClassMethod.getName());

                for (int j = 0; j < secondClassMethodsArr.length; j++) {
                    Method secondClassMethod = secondClassMethodsArr[j];
                    if(secondClassMethod.getName().equals(firstClassMethod.getName())){
                        Object secondValue = secondClassMethod.invoke(secondInstance, null);
                        if(firstValue.equals(secondValue)){
                            logger.info(" Values do match! ");
                        }
                    }
                }
            }
        }
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
}

2
Cảm ơn, nhưng chúng tôi đã có mã đi qua biểu đồ đối tượng bằng cách sử dụng phản chiếu liệt kê các thay đổi. Câu hỏi này không phải về cách thực hiện mà là cố gắng tránh phát minh lại bánh xe.
Kaypro II

Phát minh lại bánh xe không phải là xấu nếu việc phát minh lại đã xảy ra. (IE: Bánh xe được phát minh lại đã được trả tiền.)
Thomas Eding

1
Tôi đồng ý với bạn, nhưng trong trường hợp này, mã được phát minh lại là một mớ hỗn độn không thể giải thích được.
Kaypro II

3
Tôi sẽ Fieldskhông kiểm tra các phương pháp. Phương thức có thể là bất cứ thứ gì, như getRandomNumber().
Bohemian

-3

Một cách tiếp cận đơn giản hơn để nhanh chóng cho bạn biết liệu hai đối tượng có khác nhau hay không là sử dụng thư viện dấu phẩy apache

    BeanComparator lastNameComparator = new BeanComparator("lname");
    logger.info(" Match "+bc.compare(firstInstance, secondInstance));

Điều đó không hiệu quả với tôi bởi vì tôi cần biết điều gì đã thay đổi, không chỉ là có sự thay đổi ở đâu đó.
Kaypro II
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.