Nesne durumunun OO dilinde uygulanması?


11

Temel bir durum makinesinin uygulanmasını içeren bir araba yarışını simüle eden bazı Java kodları aldım. Bu klasik bir bilgisayar bilimi devlet makinesi değil, sadece birden fazla duruma sahip olabilen ve bir dizi hesaplamaya dayanarak durumları arasında geçiş yapabilen bir nesnedir.

Sadece sorunu tanımlamak için, Araba durumu için bazı sabitleri tanımlayan iç içe bir enum sınıfı olan bir Car sınıfım var (OFF, IDLE, DRIVE, REVERSE, vb.). Aynı Araba sınıfı içinde, temel olarak araçların mevcut durumunu açan, bazı hesaplamalar yapan ve daha sonra araçların durumunu değiştiren büyük bir anahtar ifadesinden oluşan bir güncelleme fonksiyonuna sahibim.

Görebildiğim kadarıyla Arabalar devleti sadece kendi sınıfında kullanılıyor.

Benim sorum şu, bu yukarıda açıklanan nitelikte bir devlet makinesinin uygulanmasıyla başa çıkmanın en iyi yolu mu? En bariz çözüm gibi geliyor, ama geçmişte her zaman "anahtar ifadelerin kötü" olduğunu duydum.

Burada görebildiğim temel sorun, daha fazla durum eklediğimizde (gerekli görülürse) switch ifadesinin muhtemelen çok büyük olabileceği ve kodun zor ve bakımı zor olabileceğidir.

Bu soruna daha iyi bir çözüm ne olabilir?


3
Açıklamanız bana bir devlet makinesi gibi gelmiyor; her biri kendi iç durumuna sahip bir grup araba nesnesi gibi geliyor. Gerçek, çalışan kodunuzu codereview.stackexchange.com adresine göndermeyi düşünün ; bu millet çalışma kodu hakkında geri bildirim sağlama konusunda çok iyi.
Robert Harvey

Belki "devlet makinesi" kelimelerin kötü bir seçimidir, ancak evet, temelde kendi iç durumlarını değiştiren bir grup araba nesnesine sahibiz. Sistem etkili bir şekilde bir UML durum diyagramı ile açıklanabilir, bu yüzden yazımı bu şekilde adlandırdım. Geçmişte, sorunu tanımlamanın en iyi yolu bu değil, yazımı düzenleyeceğim.
PythonNewb

1
Hala kodunuzu kod görüntülemeye göndermeyi düşünmeniz gerektiğini düşünüyorum.
Robert Harvey

1
Bana bir devlet makinesi gibi geliyor. object.state = object.function(object.state);
robert bristow-johnson

Şimdiye kadar verilen tüm cevaplar, kabul edilen cevap da dahil olmak üzere, geçiş ifadelerinin kötü kabul edilmesinin ana nedenini kaçırmaktadır. Açık / kapalı prensibine bağlı kalmazlar.
Dunk

Yanıtlar:


13
  • Devlet Paternini kullanarak Arabayı bir tür devlet makinesine dönüştürdüm . Durum seçimi için uyarı no switchveya if-then-elseifadeler kullanılır.

  • Bu durumlarda tüm devletler iç sınıflardır, ancak başka türlü uygulanabilir.

  • Her durum, değiştirebileceği geçerli durumları içerir.

  • Birden fazla olasının olması durumunda kullanıcıdan bir sonraki durumu sorması veya yalnızca bir tanesinin mümkün olması durumunda onaylaması istenir.

  • Bunu derleyebilir ve test etmek için çalıştırabilirsiniz.

  • Bir grafik iletişim kutusu kullandım çünkü Eclipse'de etkileşimli olarak çalıştırmak daha kolaydı.

resim açıklamasını buraya girin

UML diyagramı buradan alınır .

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.JOptionPane;

public class Car {

    private State state;
    public static final int ST_OFF=0;
    public static final int ST_IDDLE=1;
    public static final int ST_DRIVE=2;
    public static final int ST_REVERSE=3;

    Map<Integer,State> states=new HashMap<Integer,State>();

    public Car(){
        this.states.put(Car.ST_OFF, new Off());
        this.states.put(Car.ST_IDDLE, new Idle());
        this.states.put(Car.ST_DRIVE, new Drive());
        this.states.put(Car.ST_REVERSE, new Reverse()); 
        this.state=this.states.get(Car.ST_OFF);
    }

    private abstract class State{

        protected List<Integer> nextStates = new ArrayList<Integer>();

        public abstract void handle();
        public abstract void change();

        protected State promptForState(String prompt){
            State s = state;
            String word = JOptionPane.showInputDialog(prompt);
            int ch = -1;
            try {
                ch = Integer.parseInt(word);
            }catch (NumberFormatException e) {
            }   

            if (this.nextStates.contains(ch)){
                s=states.get(ch);
            } else {
                System.out.println("Invalid option");
            }
            return s;               
        }       

    }

    private class Off extends State{

        public Off(){ 
            super.nextStates.add(Car.ST_IDDLE);             
        }

        public void handle() { System.out.println("Stopped");}

        public void change() {
            state = this.promptForState("Stopped, iddle="+Car.ST_IDDLE+": ");
        }

    }

    private class Idle extends State{
        private List<Integer> nextStates = new ArrayList<Integer>();
        public Idle(){
            super.nextStates.add(Car.ST_DRIVE);
            super.nextStates.add(Car.ST_REVERSE);
            super.nextStates.add(Car.ST_OFF);       
        }

        public void handle() {  System.out.println("Idling");}

        public void change() { 
            state=this.promptForState("Idling, enter 0=off 2=drive 3=reverse: ");
        }

    }

    private class Drive extends State{

        private List<Integer> nextStates = new ArrayList<Integer>();
        public Drive(){
            super.nextStates.add(Car.ST_IDDLE);
        }       
        public void handle() {System.out.println("Driving");}

        public void change() {
            state=this.promptForState("Idling, enter 1=iddle: ");
        }       
    }

    private class Reverse extends State{
        private List<Integer> nextStates = new ArrayList<Integer>();
        public Reverse(){ 
            super.nextStates.add(Car.ST_IDDLE);
        }           
        public void handle() {System.out.println("Reversing");} 

        public void change() {
            state = this.promptForState("Reversing, enter 1=iddle: ");
        }       
    }

    public void request(){
        this.state.handle();
    }

    public void changeState(){
        this.state.change();
    }

    public static void main (String args[]){
        Car c = new Car();
        c.request(); //car is stopped
        c.changeState();
        c.request(); // car is iddling
        c.changeState(); // prompts for next state
        c.request(); 
        c.changeState();
        c.request();    
        c.changeState();
        c.request();        
    }

}

1
Bunu gerçekten beğendim. En iyi yanıtı takdir ediyorum ve anahtar ifadelerinin savunması (şimdi sonsuza kadar hatırlayacağım) olsa da, bu model fikrini gerçekten seviyorum. Teşekkür ederim
PythonNewb

@PythonNewb Çalıştırdınız mı?
Tulains Córdova

Evet, mükemmel çalışıyor. Uygulama sahip olduğum kod için biraz farklı olacak, ama genel fikir harika. Devlet sınıflarını çevreleyen sınıfın dışına taşımayı düşünebilirim.
PythonNewb

1
@PythonNewb Bir arabirim yerine soyut bir sınıf kullanarak giriş mantığı için değişiklik durumunu / istemini yeniden kullanarak kodu daha kısa bir sürüme değiştirdim. 20 satır daha kısa ama test ettim ve aynı şekilde çalışıyorum. Her zaman eski, daha uzun sürümü düzenleme geçmişine bakarak alabilirsiniz.
Tulains Córdova

1
@Caleth Aslında bunu böyle yazdım çünkü bunu gerçek hayatta yapıyorum, yani değiştirilebilir parçaları haritalarda saklıyorum ve bunları bir parametre dosyasından yüklenen kimliklere dayanarak alıyorum. Genellikle haritalarda sakladığım şey nesnelerin kendileri değil, nesneler pahalıysa veya çok fazla statik olmayan durumları varsa yaratıcılarıdır.
Tulains Córdova

16

anahtar ifadeleri bozuk

Nesne yönelimli programlamaya kötü bir isim veren bu tür bir aşırı basitleştirme. Kullanmak ifbir switch deyimi kullanmak kadar "kötü". Her iki durumda da polimorfik olarak gönderim yapmıyorsunuz.

Bir ses ısırığına uyan bir kuralınız varsa, bunu deneyin:

Switch ifadeleri, iki kopyasına sahip olduğunuz anda çok kötü hale gelir.

Kod tabanında başka hiçbir yerde çoğaltılmayan bir switch deyimi bazen kötü olmayı başarabilir. Vakalar halka açık değilse, ancak kapsüllenmişse, bu gerçekten kimsenin işi değildir. Özellikle sınıflara nasıl ve ne zaman yeniden bakacağınızı biliyorsanız. Sadece yapabileceğin demek zorunda olduğun anlamına gelmez. Çünkü artık bunu yapmanın daha az kritik olması mümkündür.

Kendinizi switch ifadesine gittikçe daha fazla şey koymaya çalışırken bulursanız, vakaların bilgisini etrafa yayarsanız veya bunun bir kopyasını oluşturmak için o kadar da kötü olmayı istemiyorsanız, vakaları ayrı sınıflara yeniden düzenleme zamanı.

Anahtar ifadelerini yeniden düzenleme hakkında birkaç ses ısırığından daha fazlasını okumak için zamanınız varsa, c2, anahtar ifadesi kokusu hakkında çok iyi dengelenmiş bir sayfaya sahiptir .

OOP kodunda bile, her anahtar kötü değildir. Bunu nasıl kullandığınız ve neden.


2

Araba bir tür devlet makinesidir. Anahtar deyimleri, süper durumlardan ve alt durumlardan yoksun bir durum makinesini uygulamanın en basit yoludur.


2

Anahtar ifadeleri kötü değil. "Anahtar ifadeleri kötü" gibi şeyler söyleyen insanları dinlemeyin! Switch ifadelerinin bazı özel kullanımları, alt sınıflamayı taklit etmek için switch kullanmak gibi bir antipatterndir. (Ama bu antipattern'i if's ile de uygulayabilirsiniz, bu yüzden sanırım da kötüdür!).

Uygulamanız kulağa hoş geliyor. Eğer çok daha fazla devlet eklerseniz korumak zor olacak doğru olduğunu. Ancak bu sadece bir uygulama meselesi değildir - farklı davranışlara sahip birçok devlete sahip bir nesneye sahip olmak kendi başına bir sorundur. Arabanızın 25 eyalete sahip olduğunu görmek, her biri eyalete geçiş için farklı davranışlar ve farklı kurallar sergiledi. Sadece bu davranışı belirtmek ve belgelemek çok büyük bir iş olacaktır. Sen sahip olacak binlerce devlet geçiş kurallarının! Boyutu switchsadece daha büyük bir sorunun belirtisi olacaktır. Yani mümkünse bu yolda gitmek kaçının.

Olası bir çözüm, devleti bağımsız alt-sitelere bölmektir. Örneğin, TERS gerçekten DRIVE'dan ayrı bir durum mudur? Belki de araba durumları ikiye ayrılabilir: Motor durumu (KAPALI, Rölanti, SÜRÜCÜ) ve yön (İLERİ, TERS). Motor durumu ve yönü büyük olasılıkla çoğunlukla bağımsız olacaktır, bu nedenle mantık çoğaltma ve durum geçiş kurallarını azaltırsınız. Daha az durumu olan daha fazla nesnenin yönetilmesi, çok sayıda durumu olan tek bir nesneden çok daha kolaydır.


1

Örneğin, arabalar klasik bilgisayar bilimi anlamında sadece devlet makineleridir. Küçük, iyi tanımlanmış bir durum kümesine ve bir tür devlet geçiş mantığına sahiptirler.

İlk önerim, geçiş mantığını kendi işlevine (veya diliniz birinci sınıf işlevleri desteklemiyorsa) ayırmayı düşünmektir.

İkinci önerim, kendi işlevine (veya diliniz birinci sınıf işlevleri desteklemiyorsa, sınıf) sahip olacak olan geçiş mantığını devletin kendisine bölmeyi düşünmektir.

Her iki şemada da, devlete geçiş süreci şu şekilde görünecektir:

mycar.transition()

veya

mycar.state.transition()

İkincisi, elbette, birinci sınıf gibi görünmek için araba sınıfına sarılabilir.

Her iki senaryoda da, yeni bir durum eklemek (örneğin, TASLAK), yalnızca yeni bir durum nesnesi türü eklemeyi ve özellikle yeni duruma geçen nesneleri değiştirmeyi içerir.


0

Ne kadar büyük switcholabileceğine bağlıdır .

Örneğinizde, bence bir switchsorun yok çünkü gerçekten sahip olabileceğinizi düşünebileceğim başka bir durum Caryok, bu yüzden zamanla daha büyük olmaz.

Tek sorun, her casebirinin çok sayıda talimatın bulunduğu büyük bir anahtara sahipse , her biri için ayrı özel yöntemler yapın.

Bazen insanlar devlet tasarım modelini önerir , ancak karmaşık mantıkla uğraşırken daha uygundur ve birçok farklı işlem için farklı iş kararları veren devletler. Aksi takdirde, basit problemlerin basit çözümleri olmalıdır.

Bazı senaryolarda, yalnızca durum A veya B olduğunda, ancak C veya D değilken görevleri gerçekleştiren yöntemleriniz olabilir veya duruma bağlı çok basit işlemlerle birden çok yönteminiz olabilir. O zaman bir veya birkaç switchifade daha iyi olurdu.


0

Bu, Tasarım Desenleri yerine Nesne Tabanlı programlama yapmadan önce kullanılan türden eski tip bir devlet makinesi gibi geliyor. C gibi anahtar ifadeleri olan herhangi bir dilde uygulanabilir.

Diğerlerinin söylediği gibi, anahtar ifadelerinde doğal olarak yanlış bir şey yoktur. Alternatifler genellikle daha karmaşıktır ve anlaşılması daha zordur.

Anahtar kutusu sayısı gülünç derecede fazla olmadıkça, şey oldukça yönetilebilir kalabilir. Okunabilir kalmanın ilk adımı, her durumda kodu, durumun davranışını uygulamak için bir işlev çağrısı ile değiştirmektir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.