Là mô hình quan sát phù hợp khi các quan sát viên không độc lập với nhau?


9

Tôi có một class Cartrong đó có 2 thuộc tính: int priceboolean inStock. Nó cũng chứa một Listsố abstract class State(lớp trống). Có 2 tiểu bang có thể được áp dụng trên xe và mỗi tiểu bang được đại diện bởi lớp riêng của nó: class Upgrade extends Stateclass Shipping extends State.

A Carcó thể giữ bất kỳ số nào của mỗi trong số 2 trạng thái. Các tiểu bang có các quy tắc sau:

  • Upgrade: thêm 1vào giá cho mỗi tiểu bang áp dụng cho xe sau đó.
  • Shipping: nếu có ít nhất 1 Shippingtrạng thái trong danh sách, thì inStockđược đặt thành false.

Ví dụ: bắt đầu bằng price = 1inStock = true:

add Shipping s1    --> price: 1, inStock: false
add Upgrade g1     --> price: 1, inStock: false
add Shipping s2    --> price: 2, inStock: false
add Shipping s3    --> price: 3, inStock: false
remove Shipping s2 --> price: 2, inStock: false
remove Upgrade g1  --> price: 1, inStock: false
remove Shipping s1 --> price: 1, inStock: false
remove Shipping s3 --> price: 1, inStock: true

Tôi đã suy nghĩ về mẫu người quan sát trong đó mỗi thao tác thêm và xóa thông báo cho người quan sát. Tôi đã có một cái gì đó như thế này trong đầu, nhưng nó không tuân theo các quy tắc tôi đặt ra:

abstract class State implements Observer {

    public abstract void update();
}

class Car extends Observable {

    List<State> states = new ArrayList<>();
    int price = 100;
    boolean inStock = true;

    void addState(State state) {

        if (states.add(state)) {
            addObserver(state);
            setChanged();
            notifyObservers();
        }
    }

    void removeState(State state) {

        if (states.remove(state)) {
            deleteObserver(state);
            setChanged();
            notifyObservers();
        }
    }
}

class Upgrade extends State {

    @Override
    public void update(Observable o, Object arg) {

        Car c = (Car) o;
        int bonus = c.states.size() - c.states.indexOf(this) - 1;
        c.price += bonus;
        System.out.println(c.inStock + " " + c.price);
    }
}

class Shipping extends State {

    @Override
    public void update(Observable o, Object arg) {

        Car c = (Car) o;
        c.inStock = false;
        System.out.println(c.inStock + " " + c.price);
    }
}

Rõ ràng, điều này không làm việc. Khi a Shippingbị xóa, một cái gì đó phải kiểm tra xem có cài đặt trạng thái nào khác thành inStockfalse không, vì vậy việc loại bỏ Shippingkhông thể inStock = true. Upgradetăng priceở mỗi cuộc gọi. Sau đó tôi đã thêm các hằng số cho các giá trị mặc định và thử tính toán lại dựa trên các giá trị đó.

Tôi hoàn toàn không cố gắng áp đặt bất kỳ khuôn mẫu nào, tôi chỉ đang cố gắng tìm một giải pháp cho các yêu cầu trên. Lưu ý rằng trong thực tế Carcó chứa nhiều thuộc tính và có nhiều trạng thái có thể được áp dụng theo cách này. Tôi đã nghĩ về một vài cách để làm điều này:

  1. Vì mỗi người quan sát nhận được Car, nó có thể xem xét tất cả các nhà quan sát khác hiện đang đăng ký và thực hiện thay đổi dựa trên điều đó. Tôi không biết liệu có thông minh khi vướng vào những người quan sát như thế này không.
  2. Khi một người quan sát được thêm hoặc xóa trong Carđó, sẽ có một tính toán lại. Tuy nhiên, việc tính toán lại này sẽ phải được thực hiện trên tất cả các nhà quan sát bất kể cái nào vừa được thêm / xóa.
  3. Có một lớp "trình quản lý" bên ngoài sẽ gọi các phương thức thêm và xóa và thực hiện tính toán lại.

Một mẫu thiết kế tốt để thực hiện hành vi được mô tả là gì và nó sẽ hoạt động như thế nào?


1
Điểm của việc trừu tượng hóa Bang thành lớp riêng của nó là tách ra Logic không tương tác với nhau, đơn giản hóa mã. Tuy nhiên, logic kinh doanh của bạn chỉ ra rằng Logic của họ được liên kết, và do đó, bạn phải quay lại liên kết, dẫn đến sự lộn xộn khủng khiếp này của Chúa. Đây không phải là mẫu quan sát là vấn đề ở đây, đó là mẫu Vai trò bạn đang áp dụng với Bang.
ArTs

Làm thế nào bạn sẽ giải quyết vấn đề nếu bạn đang làm nó bằng tay?
James Youngman

@JamesYoungman Tôi đã kết thúc bằng cách sử dụng tùy chọn thứ 3 của mình - một người quản lý bên ngoài. Các quy tắc viết trên giấy cho trường hợp này rất đơn giản, nhưng các tùy chọn mà ngôn ngữ cung cấp cho bạn để thực hiện chúng bị hạn chế trong trường hợp này . Do đó cần một mẫu thiết kế. Suy nghĩ về "làm thế nào bạn sẽ làm điều đó bằng tay" hoạt động nhiều hơn cho các thuật toán hơn là áp dụng một bộ quy tắc rõ ràng.
dùng1804551

@ user1804551 Bạn đã chọn tốt.
Tulains Córdova ngày 18/8/2016

Có một xử lý sự kiện cho tất cả các sự kiện. Trình xử lý này chỉ đơn giản là điểm vào để tính lại trạng thái hoàn chỉnh của đối tượng. Đó là một vấn đề cổ điển. Bạn thấy biểu hiện của nó khi làm việc một hình thức "từ trên xuống dưới, từ trái sang phải" - mọi thứ đều ổn, nhưng sau đó thay đổi một cái gì đó ở giữa không được tính toán lại một cách chính xác. Nếu cuối cùng bạn hỏi "làm thế nào tôi có thể đảm bảo trật tự xử lý sự kiện?", Thì bây giờ bạn biết bạn phải làm gì.
radarbob

Câu trả lời:


1

Các nhà quan sát sẽ làm việc tuyệt vời nếu bạn yếu tố hệ thống khác nhau. Thay vì biến các trạng thái thành người quan sát, bạn có thể biến 2 lớp mới thành "người quan sát thay đổi trạng thái": một người quan sát sẽ cập nhật "giá", một lớp khác sẽ cập nhật "inStock". Theo cách này, họ sẽ độc lập nếu bạn không có quy tắc về giá tùy thuộc vào inStock hoặc ngược lại, tức là nếu mọi thứ có thể được tính bằng cách chỉ nhìn vào thay đổi trạng thái. Kỹ thuật này được gọi là "tìm nguồn cung ứng sự kiện" (ví dụ xem - https://ookami86.github.io/event-source-in-practice/ ). Đó là một mô hình trong lập trình có một số ứng dụng đáng chú ý.

Trả lời cho một câu hỏi chung chung hơn, đôi khi bạn thực sự có sự phụ thuộc giữa các nhà quan sát. Ví dụ, bạn có thể muốn một người quan sát phản ứng trước người khác. Trong trường hợp như vậy, thông thường có thể thực hiện tùy chỉnh lớp Quan sát để xử lý đơn hàng hoặc phụ thuộc.


0

Tôi đã kết thúc với tùy chọn 3 - sử dụng trình quản lý bên ngoài. Người quản lý có trách nhiệm thêm và xóa States khỏi Cars và thông báo cho người quan sát khi những thay đổi này xảy ra.

Đây là cách tôi sửa đổi mã. Tôi đã xóa Observable/ Observercủa JDK vì tôi đang tự thực hiện.

Mỗi cái Stategiữ một tham chiếu đến Carứng dụng của nó.

abstract class State {

    Car car;

    State(Card car) { this.car = car; }

    public abstract void update();
}

class Upgrade extends State {

    @Override
    public void update() {

        int bonus = car.states.size() - car.states.indexOf(this) - 1;
        car.price += bonus;
        System.out.println(car.inStock + " " + car.price);
    }
}

class Shipping extends State {

    @Override
    public void update() {

        car.inStock = false;
        System.out.println(car.inStock + " " + car.price);
    }
}

Carchỉ giữ trạng thái của nó (để tránh nhầm lẫn: thuộc tính) và không xử lý thêm và xóa States:

class Car extends Observable {

    List<State> states = new ArrayList<>();
    int price = 100;
    boolean inStock = true;
}

Đây là người quản lý. Nó đã vượt qua Carcông việc (có thể quan sát) để quản lý State(người quan sát) của nó.

class StatesManager {

    public void addState(Card car, State state) {

        car.states.add(state);
        for (State state : car. states)
            state.update;
    }

    public void removeState(Card car, State state) {

        car.states.remove(state);
        for (State state : car. states)
            state.update;
    }
}

Một số điều cần ghi nhớ:

  • Tất cả các nhà quan sát được thông báo về mọi thay đổi. Một lược đồ phân phối sự kiện thông minh hơn có thể loại bỏ các cuộc gọi không cần thiết đến updatephương thức của người quan sát .
  • Các nhà quan sát có thể muốn đưa ra các phương pháp giống như "cập nhật" hơn cho các dịp khác nhau. Chỉ là một ví dụ, họ có thể chia updatephương thức hiện tại thành updateOnAddupdateOnRemovenếu họ chỉ quan tâm đến một trong những thay đổi này. Sau đó, addStateremoveStatephương pháp sẽ được cập nhật cho phù hợp. Cùng với điểm trước, phương pháp này có thể kết thúc như một cơ chế mạnh mẽ, có thể mở rộng và linh hoạt.
  • Tôi đã không chỉ định điều gì đưa ra hướng dẫn để thêm và xóa States và khi điều đó xảy ra vì nó không quan trọng đối với câu hỏi. Tuy nhiên, trong trường hợp của câu trả lời này, có những điểm sau đây để xem xét. Vì Statebây giờ phải được tạo bằng Car(không có hàm tạo trống nào được hiển thị) trước khi gọi phương thức của người quản lý, các phương thức addStateremoveStatekhông cần phải lấy Carvà chỉ có thể đọc từ đó state.car.
  • Các quan sát viên được thông báo theo thứ tự đăng ký theo quan sát theo mặc định. Một thứ tự khác nhau có thể được chỉ định.
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.