Komut deseni tasarımı


11

Komut modelinin bu eski uygulamasına sahibim. Bu, tüm DIOperation uygulaması boyunca bir Bağlamdan geçmektir , ancak daha sonra, öğrenme ve öğrenme sürecinde (asla durmaz), bunun optimal olmadığını fark ettim. Ben de burada "ziyaret" gerçekten uygun olmadığını ve sadece karışık olduğunu düşünüyorum.

Aslında benim kodumu yeniden düzenlemeyi düşünüyorum, çünkü bir Komut başkaları hakkında hiçbir şey bilmemeli ve şu anda hepsi aynı anahtar / değer çiftlerini paylaşıyor. Hangi sınıfın hangi anahtar / değer çiftine sahip olduğunu ve bazen yinelenen değişkenlere yol açtığını korumak gerçekten zordur.

Kullanım örneği: diyelim ki CommandB , CommandA tarafından ayarlanan KullanıcıAdı gerektiriyor . CommandA, UserNameForCommandB = John anahtarını ayarlamalı mı? Yoksa ortak bir UserName = John anahtar / değer paylaşmalı mı? KullanıcıAdı üçüncü bir Komut tarafından kullanılıyorsa ne olur?

Bu tasarımı nasıl geliştirebilirim? Teşekkürler!

class DIParameters {
public:
   /**
    * Parameter setter.
    */
    virtual void setParameter(std::string key, std::string value) = 0;
    /**
    * Parameter getter.
    */
    virtual std::string getParameter(std::string key) const = 0;

    virtual ~DIParameters() = 0;
};

class DIOperation {
public:
    /**
     * Visit before performing execution.
     */
    virtual void visitBefore(DIParameters& visitee) = 0;
    /**
     * Perform.
     */
    virtual int perform() = 0;
    /**
     * Visit after performing execution.
     */
    virtual void visitAfter(DIParameters& visitee) = 0;

    virtual ~DIOperation() = 0;
};

3
Asla özellikleri (isim gibi) ayarlamak için komut kullanarak şans oldu. Çok bağımlı olmaya başlar. Ayar özellikleriniz bir olay mimarisi veya gözlemci deseni kullanmayı deneyin.
ahenderson

1
1. Parametreleri neden ayrı bir ziyaretçiden geçiriyoruz? Bir bağlamı icra argümanı olarak geçirmenin nesi yanlış? 2. Bağlam komutun 'ortak' kısmı içindir (örn. Geçerli oturum / belge). İşleme özgü tüm parametreler, işlemin yapıcısından daha iyi geçirilir.
Kris Van Bael

@KrisVanBael, değiştirmeye çalıştığım kafa karıştırıcı kısım. Aslında bir Bağlam iken Ziyaretçi olarak geçiyorum ...
Andrea Richiardi

@ahenderson Komutlarım arasındaki olayları mı demek istediniz? Anahtar / değer çiftlerinizi buraya koyar mısınız (Android'in Parcel ile yaptığı şeye benzer)? CommandA'nın CommandB'nin kabul ettiği anahtar / değer çiftleriyle bir Etkinlik oluşturması açısından aynı mı olurdu?
Andrea Richiardi

Yanıtlar:


2

Komut parametrelerinizin değişebilirliği konusunda biraz endişeliyim. Sürekli değişen parametrelerle bir komut oluşturmak gerçekten gerekli mi?

Yaklaşımınızla ilgili sorunlar:

Devam ederken diğer evreler / komutların parametrelerinizi değiştirmesini ister misiniz perform?

İstediğiniz musunuz visitBeforeve visitAfteraynı Commandnesneye farklı ile çağrılacak DIParameternesneleri?

Birisinin komutlarınızla ilgili hiçbir fikri olmayan parametreleri beslemesini ister misiniz?

Mevcut tasarımınız tarafından bunların hiçbiri yasaklanmamıştır. Genel bir anahtar / değer parametresi kavramı zaman zaman kendi değerlerine sahip olsa da, genel bir komut sınıfına göre bunu sevmiyorum.

Sonuçlara örnek:

CommandSınıfınızın somut bir gerçekleşmesini düşünün - böyle bir şey CreateUserCommand. Açıkçası, yeni bir kullanıcının oluşturulmasını istediğinizde, komutun o kullanıcı için bir ada ihtiyacı olacaktır. Biliyorum olduğu göz önüne alındığında CreateUserCommandve DIParameterssınıfları, hangi parametrenin ben ayarlarım?

userNameParametreyi ayarlayabilirim veya username.. parametreleri büyük / küçük harfe duyarsız mı? Gerçekten bilmiyorum .. oh bekle .. belki sadece name?

Gördüğünüz gibi, genel bir anahtar / değer eşlemesinden elde ettiğiniz özgürlüğü, sınıflarınızı bunları uygulamayan biri olarak kullanmanın istemeden zor olduğunu ima eder. En azından komutların hangi komutları desteklediğini bildirmek için bazı sabitler sağlamanız gerekir.

Olası farklı tasarım yaklaşımları:

  • Değişmez parametreler: ParameterÖrneklerinizi değiştirilemez hale getirerek bunları farklı komutlar arasında özgürce kullanabilirsiniz.
  • Belirli parametre sınıfları: UserParameterBir kullanıcıyı içeren komutlar için tam olarak ihtiyacım olan parametreleri içeren bir sınıf göz önüne alındığında , bu API ile çalışmak çok daha kolay olacaktır. Parametreler üzerinde hala kalıtım olabilir, ancak komut sınıflarının keyfi parametreler alması artık mantıklı olmaz - elbette bu, API kullanıcılarının hangi parametrelerin tam olarak gerekli olduğunu bildikleri anlamına gelir .
  • Bağlamda başına bir komut örneği: Eğer gibi şeyler olması komutlarınızı gerekiyorsa visitBeforeve visitAfteraynı zamanda farklı parametrelerle tekrar kullanmadan iken, sen farklı parametrelerle denilen alma sorununa açık olacaktır. Parametrelerin birden fazla yöntem çağrısında aynı olması gerekiyorsa, bunları, çağrılar arasındaki diğer parametreler için kapatılamayacak şekilde komuta dahil etmelisiniz.

Evet, ziyaretten önce kurtuldum ve sonra ziyaret ettim. Temelde perform yönteminde DIParameter arayüzümü geçiyorum. İstenmeyen DIParamters örnekleriyle ilgili sorun her zaman orada olacak, çünkü arayüzü geçme esnekliğine sahip olmayı seçtim. Doldurulduktan sonra DIParameters çocuklarını değiştiremez hale getirme fikrini gerçekten seviyorum. Ancak, bir "merkezi otorite" hala doğru DIParametreyi Komuta iletmelidir. Muhtemelen bu yüzden bir Ziyaretçi kalıbı uygulamaya başladım ... Bir şekilde kontrolü tersine çevirmek istedim ...
Andrea Richiardi

0

Tasarım ilkeleri hakkında güzel olan şey, er ya da geç birbirleriyle çatışmalarıdır.

Açıklanan durumda, her komutun bilgi alabileceği ve bilgi koyabileceği bir tür bağlamla gitmeyi tercih ederim (özellikle bunlar anahtar / değer çiftleri ise). Bu bir değiş tokuşa dayanır: Birbirine bir çeşit girdi oldukları için ayrı komutların birleştirilmesini istemiyorum. CommandB içinde, UserName'in nasıl ayarlandığını umursamıyorum - sadece kullanmam için orada olması. CommandA'da da aynı şey var: Bilgileri girdim, başkalarının onunla ne yaptığını bilmek istemiyorum - ikisi de kim olduklarını.

Bu, kötü bulabileceğiniz bir tür geçiş bağlamı anlamına gelir. Benim için alternatif daha kötü, özellikle de bu basit anahtar / değer bağlamı (alıcılar ve ayarlayıcılarla basit bir fasulye olabilir, "serbest form" faktörünü biraz sınırlamak), çözümün basit ve test edilebilir olmasına izin verebilirse, her biri kendi iş mantığına sahip ayrı komutlar.


1
Burada hangi ilkeleri çelişiyorsunuz?
Jimmy Hoffa

Açıklığa kavuşturmak gerekirse, sorunum Bağlam veya Ziyaretçi kalıbı arasında seçim yapmak değil. Temelde Ziyaretçi adlı bir Bağlam kalıbı kullanıyorum :)
Andrea Richiardi

Tamam, muhtemelen sorunuzu / probleminizi yanlış anladım.
Martin

0

Bir komut arayüzünüz olduğunu varsayalım:

class Command {
public:
    void execute() = 0;
};

Ve bir konu:

class Subject {
    std::string name;
public:
    void setName(const std::string& name) {this->name = name;}
}

İhtiyacınız olan şey:

class NameObserver {
public:
    void update(const std::string& name) = 0;
};

class Subject {
    NameObserver& o;
    std::string name;
private:
    void setName(const std::string& name) {
        this->name = name;
        o.update(name);
    }
};

class CommandB: public Command, public NameObserver {
    std::string name;
public:
    void execute();
    void update(const std::string& name) {
        this->name = name;
        execute();
    }
};

NameObserver& oCommandB başvuru olarak ayarlayın . Artık CommandA, Subject adını değiştirdiğinde CommandB doğru bilgilerle çalışabilir. Ad daha fazla komut tarafından kullanılıyorsa,std::list<NameObserver>


Cevap için teşekkürler. Bu tasarım imho ile ilgili sorun, her parametre için bir Setter + NameObserver ihtiyacımız var. Bir DIParameters (bağlam) örneğini geçebilir ve bildirebilirim, ancak yine de muhtemelen CommandA'yı CommandB ile birleştirdiğim gerçeğini çözmeyeceğim, yani CommandA'nın sadece CommandB'nin bilmesi gereken bir anahtar değeri koyması gerekiyor ... ne denedim de hangi Komut hangi parametre ihtiyacı ayarlar ve DIParameters örneğine göre alır / almak için tek olan bir dış varlık (ParameterHandler) oldu.
Andrea Richiardi

@Kap "Bu tasarım imho ile ilgili sorun her parametre için bir Setter + NameObserver ihtiyacımız olmasıdır" - bu bağlamda parametre benim için biraz kafa karıştırıcı, alan demek istediğini düşünüyorum. Bu durumda, değişen her alan için zaten bir ayarlayıcıya sahip olmanız gerekir. Örneğinizden ComamndA, Konunun adını değiştiriyor gibi görünüyor. Bir ayarlayıcı ile alanı değiştirmelidir. Not: alan başına bir gözlemciye ihtiyacınız yoktur, sadece bir alıcıya sahip olun ve nesneyi tüm gözlemcilere iletin.
ahenderson

0

Bu Programcılar (bu durumda özür dilerim) ele doğru yolu olup olmadığını bilmiyorum, ama, tüm cevapları burada kontrol ettikten sonra (özellikle @ Frank's). Kodumu şu şekilde yeniden düzenledim:

  • Düşen DİParametreler. DIOperation'ın girişi (değişmez) olarak bireysel (genel) nesnelerim olacak. Misal:
class RelatedObjectTriplet {
özel:
    std :: string const m_sPrimaryObjectId;
    std :: string const m_sSeincilObjectId;
    std :: string const m_sRelationObjectId;

    RelatedObjectTriplet & operator = (RelatedObjectTriplet diğer);

halka açık:
    RelatedObjectTriplet (std :: string const & sPrimaryObjectId,
                         std :: string const & sSeincilObjectId,
                         std :: string const & sRelationObjectId);

    RelatedObjectTriplet (RelatedObjectTriplet const & diğer);


    std :: string const & getPrimaryObjectId () const;
    std :: string const & getSeincilObjectId () const;
    std :: string const & getRelationObjectId () const;

    ~ RelatedObjectTriplet ();
};
  • Yeni DIOperation sınıfı (örnekle) şu şekilde tanımlanır:
şablon <sınıf T = geçersiz> 
sınıf DIOperation {
halka açık:
    sanal int perform () = 0;

    sanal T getResult () = 0;

    sanal ~ DIOperation () = 0;
};

class CreateRelation: genel DIOperation <RelatedObjectTriplet> {
özel:
    statik std :: dize sabit TYPE;

    // Parametreler (değişmez)
    RelatedObjectTriplet const m_sParams;

    // Gizli
    CreateRelation & operator = (CreateRelation sabit ve kaynağı);
    CreateRelation (CreateRelation sabit ve kaynak);

    // Dahili
    std :: string m_sNewRelationId;

halka açık:
    CreateRelation (RelatedObjectTriplet const & params);

    int perform ();

    RelatedObjectTriplet getResult ();

    ~ CreateRelation ();
};
  • Bu şekilde kullanılabilir:
RelatedObjectTriplet triplet ("33333", "55555", "77777");
CreateRelation createRel (üçlü);
createRel.perform ();
const RelatedObjectTriplet res = createRel.getResult ();

Yardım için teşekkürler ve umarım burada hata yapmadım :)

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.