SORU
25 ŞUBAT 2011, Cuma


Nasıl factory method deseni uygulamak için C doğru

Gerçekten basit geliyor olsa da, bunu yapmak için nasıl bilmiyorum, çünkü beni rahatsız oldukça uzun bir süre için yapım, olmuştur: C tek bir şey var

Nasıl C Fabrika Yöntemi doğru uygulamak?

Amaç: mümkün istemci bazı nesne, kabul edilemez sonuçlarını ve performans isabet olmadan. nesnenin kurucular yerine fabrika yöntemlerle oluşturmaya izin vermek için yapmak

"Fabrika yöntemini", yani bir nesnenin içinde statik fabrika metotları veya yöntemleri her iki sınıf, ya da başka bir küresel fonksiyonları tanımlanmış. desen "Sınıf örnekleme normal şekilde yönlendirme kavramı X yapıcı daha başka bir yerde". sadece genel olarak

Benim aklıma bazı olası cevaplar dolaşmanızı sağlar.


0) fabrikalar yapmaz, kurucular olun.

Bu güzel (ve aslında çoğu zaman en iyi çözüm) sesler, ama genel bir çözüm değildir. İlk olarak, nesne bir inşaat işi başka bir sınıf için çıkarma haklı çıkarmak için karmaşık durumlar vardır. Ama bile bu gerçeği bir kenara koyarak, basit nesneleri sadece kurucular kullanarak sık sık yapmayacağım bile.

Biliyorum en basit örnek 2-B Vektör sınıfı. Bu kadar basit, zor henüz. Hem Kartezyen ve kutupsal koordinatlarda hem de onu inşa etmek mümkün olmak istiyorum. Belli ki, bunu yapamam:

struct Vec2 {
    Vec2(float x, float y);
    Vec2(float angle, float magnitude); // not a valid overload!
    // ...
};

Düşünme benim doğal yolu vardır:

struct Vec2 {
    static Vec2 fromLinear(float x, float y);
    static Vec2 fromPolar(float angle, float magnitude);
    // ...
};

, Kurucular yerine, bir şekilde fabrika desen uygulama olduğumu aslında anlamına gelen statik fabrika metotlarının kullanımı..., aklıma şunu getiriyor ("sınıfın kendi fabrika olur"). Bu güzel görünüyor (ve bu özel durumda uygun olurdu), ama noktası 2'de tarif edeceğim bazı durumlarda, başarısız. Okuyun.

başka bir örnek: çalışırken aşırı yük iki opak typedefs bazı API (gibi GUID ilgisiz etki alanı veya bir GUID ve bir bit), tür anlamsal olarak tamamen farklı (yani teoride geçerli aşırı) ama aslında etmeli aynı şey gibi işaretsiz değer vermez ya da void işaretçiler.


1) Java

Java basit, biz sadece dinamik ayrılan nesneler gibi. Bir fabrika yapımı için önemsiz gibi

class FooFactory {
    public Foo createFooInSomeWay() {
        // can be a static method as well,
        //  if we don't need the factory to provide its own object semantics
        //  and just serve as a group of methods
        return new Foo(some, args);
    }
}

C , bu çevirir:

class FooFactory {
public:
    Foo* createFooInSomeWay() {
        return new Foo(some, args);
    }
};

Serin? Sık sık, gerçekten. Ama sonra bu kullanıcı sadece dinamik ayırma kullanmaya zorlar. Statik ayırma C karmaşık hale getiren, fakat aynı zamanda sık sık onu güçlü kılıyor. Ayrıca, bazı hedefler var inanıyorum (anahtar kelime: gömülü) dinamik ayrılması için izin vermez. Ve bu platformların kullanıcıları temiz OOP yazmak anlamına gelmez.

Her neyse, felsefe bir yana: Genel durumda, fabrikanın kullanıcılar dinamik ayırma için ölçülü olmaya zorlamak istemiyorum.


2) değerini Döndürür

Yani TAMAM, 1) dinamik ayırma istediğimiz zaman güzel. Neden üstüne statik ayırma Ekle değil mi?

class FooFactory {
public:
    Foo* createFooInSomeWay() {
        return new Foo(some, args);
    }
    Foo createFooInSomeWay() {
        return Foo(some, args);
    }
};

Ne? Dönüş tipi aşırı miyiz? Oh, tabii ki edemeyiz. Hadi yöntem adlarını yansıtacak şekilde değiştirin. Ve evet, ben yazdım geçersiz kod örneği, yukarıda sadece stres ne kadar sevmesem de gerek değiştirmek için yöntem adı, örneğin, çünkü biz uygulayamıyor dil-agnostik fabrika tasarımı düzgün şimdi beri bunu değiştirmeliyiz isimleri - ve her kullanıcı bu kodu gerekir hatırlamak fark uygulanmasından belirtimi.

class FooFactory {
public:
    Foo* createDynamicFooInSomeWay() {
        return new Foo(some, args);
    }
    Foo createFooObjectInSomeWay() {
        return Foo(some, args);
    }
};

TAMAM... işte bu. Yöntem adını değiştirmeliyiz gibi çirkin değil. Aynı kodu iki kere yazmalıyız beri eksik. Ama bir kere oldu mu, işe yarıyor. Değil mi?

Evet, genellikle. Ama bazen yok. Oluştururken Foo, aslında bağımlı derleyici için dönüş değeri optimizasyonu bizim için, çünkü C standart hayırsever yeterli derleyici satıcıları değil belirtmek ne zaman oluşturulan nesne yerinde ve ne zaman olacak kopyalanan, dönen bir geçici nesne tarafından değer C . Eğer Foo kopyalamak için pahalı ise, bu yaklaşım riskli.

Ve eğer copiable hiç Foo değilse ne? Peki, doh.

Sonuç: bir nesneyi döndürerek bir fabrika Yapmak gerçekten de bazı durumlarda çözüm (2-D daha önce de belirttiğim vektör gibi), ama yine de üreticiler için genel bir yedek değil.


3) İki fazlı yapı

Muhtemelen biri ile geleceğini başka bir şey nesne ayırma ve hazırlama sorunu ayıran. Bu genellikle böyle bir kod verir

class Foo {
public:
    Foo() {
        // empty or almost empty
    }
    // ...
};

class FooFactory {
public:
    void createFooInSomeWay(Foo& foo, some, args);
};

void clientCode() {
    Foo staticFoo;
    auto_ptr<Foo> dynamicFoo = new Foo();
    FooFactory factory;
    factory.createFooInSomeWay(&staticFoo);
    factory.createFooInSomeWay(&dynamicFoo.get());
    // ...
}

Bir cazibe gibi çalışır. Tek fiyat bizim kod için ödeme...

Tüm bu yazılı ve son olarak bu kaldı çünkü ben de nefret etmeliyim. :) Neden?

Öncelikle... umarım iki fazlı yapı kavramını sevmediğim ve bunu kullandığım zaman kendimi suçlu hissediyorum. Ben iddia ile benim nesne tasarımı ise "eğer varsa, geçerli bir durumda" benim kod daha güvenli ve daha az hata eğilimli olduğunu hissediyorum. Ben böyle seviyorum.

Bu Sözleşme, bırak zorunda VE sadece fabrika yapımı için bir nesne tasarımı değişiyor.. iyi, hantal.

Yukarıda birçok kişi ikna olmayacağını biliyorum, hadi bana biraz daha sağlam argümanlar verin. İki aşamalı bir yapı kullanarak, olamaz:

  • const referans üye değişkenleri, yeniden
  • bağımsız sınıf kurucular ve üye nesne kurucular tabanına geçirin.

Ve muhtemelen şu anda düşünemiyorum ki bazı sakıncaları olabilir, ve hatta özellikle zorundadır üzeri çizili beri beni çoktan ikna etmek istemiyorum.

Yani: bir fabrika uygulamak için iyi bir genel çözüm yakın bile değil.


Sonuç:

Nesne örnekleme bir yol var ki: istiyoruz

  • üniforma örnekleme ayırma ne olursa olsun, izin ver
  • inşaat yöntemleri farklı, anlamlı isimler (dayanarak bağımsız böylece aşırı değil), ver
  • önemli performans isabet tanıtmak ve tercihen önemli kod kabartmak bir hit, özellikle istemci tarafında,
  • genel olarak: herhangi bir sınıf için tanıttı olabilir.

Bahsettiğim yolu bu şartları yerine getirmek olmadığını kanıtladım sanırım.

Herhangi bir ipucu? Lütfen bir çözüm ile bana sağlamak, bu dili doğru düzgün beni böyle önemsiz bir kavram uygulamak için izin vermez olduğunu düşünmek istemiyorum.

CEVAP
25 ŞUBAT 2011, Cuma


Öncelikle, vaka olduğunda nesne yapımı bir görev karmaşık. yeterince kendi çıkarımı haklı başka bir sınıf.

Bu noktada yanlış olduğuna inanıyorum. Karmaşıklığı gerçekten önemli değil. Alaka yok. Eğer bir nesne bir adım inşa (builder deseni gibi), yapıcı bunu yapmak için doğru yerdir. Eğer gerçekten başka bir sınıf işi gerçekleştirmek için gerekiyorsa, o zaman yapıcı neyse itibaren kullanılan yardımcı bir sınıf olmalı.

Vec2(float x, float y);
Vec2(float angle, float magnitude); // not a valid overload!

Bunun için kolay bir çözüm var:

struct Cartesian {
  inline Cartesian(float x, float y): x(x), y(y) {}
  float x, y;
};
struct Polar {
  inline Polar(float angle, float magnitude): angle(angle), magnitude(magnitude) {}
  float angle, magnitude;
};
Vec2(const Cartesian &cartesian);
Vec2(const Polar &polar);

Tek dezavantajı biraz ayrıntılı görünüyor

Vec2 v2(Vec2::Cartesian(3.0f, 4.0f));

Ama iyi tarafı hemen kullanıyorsun tür koordinat ve aynı zamanda kopyalama hakkında endişelenmenize gerek yok görebilirsiniz. Kopyalamak istediğiniz ve pahalı, tabii ki profil de kanıtlandığı gibi) varsa, Qt's shared classes gibi bir şey kopyalama yükü önlemek için kullanmak isteyebilirsiniz.

Ayırma türü için, fabrika desen kullanmak için ana nedeni genellikle polimorfizmi. Kurucular sanal olamaz, ve eğer yapabilselerdi bile, çok mantıklı olmaz. Statik ya da yığın ayırma kullanarak, derleyici tam boyutunu bilmek gerekiyor çünkü polimorfik bir şekilde nesneleri oluşturamazsınız. İşaretçiler ve başvurular Sadece ile çalışır. Ve bir fabrikadan bir başvuru dönen çok, çünkü bir nesnenin teknik olarak çalışmıyorolabilirbaşvuru ile silinecek, oldukça kafa karıştırıcı olabilir ve hata eğilimli, örneğin Is the practice of returning a C reference variable, evil? bkz. Göstericiler kalan tek şey vardır, ve o çok akıllı işaretçiler içerir. Diğer bir deyişle, fabrikalar dinamik ayırma ile kullanıldığında çok yararlı olduğunu, bu gibi şeyler yapabilirsiniz:

class Abstract {
  public:
    virtual void do() = 0;
};

class Factory {
  public:
    Abstract *create();
};

Factory f;
Abstract *a = f.create();
a->do();

Diğer durumlarda, fabrika sadece sizin söylediğiniz aşırı olanlar gibi küçük sorunları çözmek için yardımcı olur. Eğer düzgün bir şekilde kullanmak mümkün olsaydı güzel olurdu, ama muhtemelen mümkün olduğu kadar çok acımıyor.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Madeon

    Madeon

    31 Ocak 2010
  • thenewboston

    thenewboston

    4 ŞUBAT 2008
  • Troy Hunt

    Troy Hunt

    29 EYLÜL 2011