รูปแบบของผู้สังเกตการณ์มีความเหมาะสมหรือไม่เมื่อผู้สังเกตการณ์ไม่เป็นอิสระต่อกัน?


9

ฉันมีclass Carซึ่งมี 2 คุณสมบัติ: และint price boolean inStockนอกจากนี้ยังถือเป็นListของabstract class State(ชั้นว่าง) มี 2 รัฐซึ่งสามารถนำมาใช้ในรถและแต่ละคนจะแสดงโดยระดับของตัวเอง: และclass Upgrade extends Stateclass Shipping extends State

A Carสามารถเก็บหมายเลขใด ๆ ของแต่ละสถานะ 2 รัฐมีกฎต่อไปนี้:

  • Upgrade: เพิ่ม1ราคาสำหรับแต่ละรัฐที่ใช้กับรถยนต์หลังจากนั้น
  • Shippingถ้ามีอย่างน้อย 1 Shippingของรัฐในรายการแล้วมีการตั้งค่าinStockfalse

ตัวอย่างเช่นเริ่มต้นด้วยprice = 1และinStock = 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

ฉันกำลังคิดเกี่ยวกับรูปแบบการสังเกตการณ์ที่แต่ละการเพิ่มและลบการดำเนินการแจ้งผู้สังเกตการณ์ ฉันมีสิ่งนี้ในใจ แต่ไม่เชื่อฟังกฎที่ฉันโพสต์:

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);
    }
}

เห็นได้ชัดว่านี่ใช้งานไม่ได้ เมื่อShippingถูกลบออกบางสิ่งบางอย่างที่มีการตรวจสอบว่ามีการตั้งค่าอีกรัฐหนึ่งinStockเป็นเท็จดังนั้นการกำจัดของไม่สามารถเพียงแค่Shipping เพิ่มขึ้นทุกครั้งที่โทร ฉันเพิ่มค่าคงที่สำหรับค่าเริ่มต้นแล้วพยายามคำนวณใหม่ตามค่าเหล่านั้นinStock = trueUpgradeprice

ฉันไม่ได้พยายามกำหนดรูปแบบใด ๆ เลยฉันแค่พยายามหาวิธีแก้ปัญหาสำหรับข้อกำหนดข้างต้น โปรดทราบว่าในทางปฏิบัติCarมีคุณสมบัติมากมายและมีหลายสถานะที่สามารถใช้ในลักษณะนี้ ฉันคิดเกี่ยวกับการทำเช่นนี้:

  1. เนื่องจากผู้สังเกตการณ์แต่ละคนได้รับCarมันสามารถดูผู้สังเกตการณ์คนอื่น ๆ ที่ลงทะเบียนอยู่ในปัจจุบันและทำการเปลี่ยนแปลงตามนั้น ฉันไม่รู้ว่ามันฉลาดหรือไม่ที่จะกีดกันผู้สังเกตการณ์เช่นนี้
  2. เมื่อผู้สังเกตการณ์ถูกเพิ่มหรือลบออกCarจะมีการคำนวณใหม่ อย่างไรก็ตามการคำนวณใหม่นี้จะต้องทำกับผู้สังเกตการณ์ทุกคนโดยไม่คำนึงถึงผู้ที่เพิ่งเพิ่ม / ลบออก
  3. มีคลาส "ผู้จัดการ" ภายนอกซึ่งจะเรียกวิธีการเพิ่มและลบและทำการคำนวณใหม่

รูปแบบการออกแบบที่ดีในการใช้พฤติกรรมที่อธิบายไว้คืออะไรและทำงานอย่างไร


1
จุดที่ทำให้เป็นนามธรรมออกไปสู่ชั้นเรียนของตัวเองคือการแยกลอจิกที่ไม่ได้มีปฏิสัมพันธ์ซึ่งกันและกันทำให้รหัสง่ายขึ้น อย่างไรก็ตามตรรกะทางธุรกิจของคุณบอกว่าตรรกะของพวกเขาเชื่อมโยงและด้วยเหตุนี้คุณต้องดิ่งในการเชื่อมโยงส่งผลให้พระเจ้านี้ระเบียบไม่ดี ไม่ใช่รูปแบบผู้สังเกตการณ์ที่เป็นปัญหาที่นี่ แต่เป็นรูปแบบบทบาทที่คุณใช้กับรัฐ
ArTs

คุณจะแก้ปัญหาอย่างไรถ้าทำด้วยมือ?
James Youngman

@JamesYoungman ฉันลงเอยด้วยการใช้ตัวเลือกที่ 3 - ผู้จัดการภายนอก กฎที่คุณเขียนลงบนกระดาษสำหรับกรณีนี้นั้นเรียบง่าย แต่ตัวเลือกภาษาที่ให้คุณสามารถนำไปใช้ได้นั้นมี จำกัดในกรณีนี้ ดังนั้นความต้องการรูปแบบการออกแบบ คิดถึง "วิธีที่คุณจะทำด้วยมือ" ทำงานกับอัลกอริธึมได้มากกว่าการใช้กฎที่ชัดเจน
user1803551

@ user1803551 คุณเลือกได้ดี
Tulains Córdova

มีตัวจัดการเหตุการณ์หนึ่งเหตุการณ์สำหรับเหตุการณ์ทั้งหมด ตัวจัดการนี้เป็นเพียงจุดเริ่มต้นสำหรับการคำนวณสถานะที่สมบูรณ์ของวัตถุอีกครั้ง มันเป็นปัญหาที่เห็นได้ชัด คุณเห็นมันชัดแจ้งเมื่อทำงานแบบฟอร์ม "จากบนลงล่างซ้ายไปขวา" - ทุกอย่างเป็น A-OK แต่การเปลี่ยนบางอย่างที่อยู่ตรงกลางนั้นไม่ได้คำนวณอย่างถูกต้อง หากคุณเคยถามว่า "ฉันจะรับประกันการจัดการกิจกรรมได้อย่างไร" ตอนนี้คุณรู้แล้วว่าคุณต้องทำอะไร
Radarbob

คำตอบ:


1

ผู้สังเกตการณ์จะทำงานได้ดีถ้าคุณแยกระบบออกจากกัน แทนที่จะทำให้ตัวเองเป็นผู้สังเกตการณ์คุณสามารถสร้าง 2 คลาสใหม่เป็น "ผู้สังเกตการณ์การเปลี่ยนสถานะ": ผู้สังเกตการณ์คนหนึ่งจะอัปเดต "ราคา" และอีกคนจะอัปเดต "inStock" ด้วยวิธีนี้พวกเขาจะเป็นอิสระถ้าคุณไม่มีกฎสำหรับราคาขึ้นอยู่กับในสต็อกหรือในทางกลับกันคือถ้าทุกอย่างสามารถคำนวณได้โดยเพียงแค่ดูที่การเปลี่ยนแปลงของรัฐ เทคนิคนี้เรียกว่า "การจัดหาเหตุการณ์" (ตัวอย่างเช่นดู - https://ookami86.github.io/event-sourcing-in-practice/ ) มันเป็นรูปแบบในการเขียนโปรแกรมที่มีแอพพลิเคชั่นเด่น ๆ

ตอบคำถามทั่วไปบางครั้งคุณมีผู้อ้างอิงระหว่างผู้สังเกตการณ์ ตัวอย่างเช่นคุณอาจต้องการให้ผู้สังเกตการณ์คนหนึ่งตอบโต้ต่อหน้าคนอื่น ในกรณีเช่นนี้เป็นไปได้ที่จะทำการปรับใช้คลาส Observable เพื่อจัดการกับการสั่งซื้อหรือการอ้างอิง


0

ฉันลงเอยด้วยตัวเลือก 3 - ใช้ผู้จัดการภายนอก ผู้จัดการมีหน้าที่รับผิดชอบในการเพิ่มและลบStates จากCars และแจ้งผู้สังเกตการณ์เมื่อมีการเปลี่ยนแปลงเหล่านี้เกิดขึ้น

นี่คือวิธีที่ฉันแก้ไขรหัส ฉันลบObservable/ Observerของ JDK เพราะฉันใช้งานของฉันเอง

แต่ละคนStateเก็บการอ้างอิงถึงที่Carใช้กับ

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);
    }
}

Carเก็บเฉพาะสถานะ (เพื่อหลีกเลี่ยงความสับสน: คุณสมบัติ) และไม่จัดการการเพิ่มและการลบStates:

class Car extends Observable {

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

นี่คือผู้จัดการ มีCarการติดตามงานในการจัดการStates (ผู้สังเกตการณ์)

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;
    }
}

สิ่งที่ควรทราบ:

  • ผู้สังเกตการณ์ทุกคนจะได้รับแจ้งเกี่ยวกับการเปลี่ยนแปลงทุกอย่าง รูปแบบการกระจายกิจกรรมที่ชาญฉลาดยิ่งขึ้นสามารถกำจัดการโทรที่ไม่จำเป็นไปยังupdateวิธีการของผู้สังเกตการณ์ได้
  • ผู้สังเกตการณ์อาจต้องการเปิดเผยวิธีการที่เหมือนกันมากขึ้นสำหรับโอกาสต่าง ๆ เช่นเดียวกับตัวอย่างพวกเขาสามารถแบ่งupdateวิธีปัจจุบันเป็นupdateOnAddและupdateOnRemoveถ้าพวกเขาสนใจเพียงหนึ่งในการเปลี่ยนแปลงเหล่านี้ จากนั้นวิธีการaddStateและremoveStateจะได้รับการปรับปรุงตาม พร้อมกับประเด็นก่อนหน้านี้วิธีการนี้สามารถกลายเป็นกลไกที่แข็งแกร่งขยายได้และมีความยืดหยุ่น
  • ฉันไม่ได้ระบุสิ่งที่ให้คำแนะนำในการเพิ่มและลบStates และเมื่อสิ่งนั้นเกิดขึ้นเนื่องจากมันไม่สำคัญสำหรับคำถาม อย่างไรก็ตามในกรณีของคำตอบนี้มีประเด็นที่ต้องพิจารณาดังต่อไปนี้ ตั้งแต่Stateตอนนี้จะต้องสร้างขึ้นด้วยCar(ไม่มีตัวสร้างที่ว่างเปล่าสัมผัส) ก่อนที่จะเรียกวิธีการของผู้จัดการที่addStateและremoveStateวิธีการไม่จำเป็นต้องใช้เวลาและก็สามารถอ่านได้จากCarstate.car
  • ผู้สังเกตการณ์จะได้รับแจ้งตามลำดับของการลงทะเบียนในสิ่งที่สังเกตได้โดยค่าเริ่มต้น คำสั่งซื้อที่แตกต่างกันสามารถระบุได้
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.