Yanıtlar:
Özel kurucuya ihtiyaç duyabileceğiniz bazı nedenler:
final
için sınıf seviyesinde olmalısınız . Özel yapıcı koymak bu nedenle neredeyse işe yaramaz.
final
. Sun'ın String sınıfı için yaptığı ve genişletilmemesi gereken API'nin makul bir bölümü budur.
Özel bir kurucu sağlayarak, sınıf örneklerinin bu sınıf dışında herhangi bir yerde oluşturulmasını önlersiniz. Bu yapıcıyı sağlamak için birkaç kullanım durumu vardır.
A. Sınıf örnekleriniz bir static
yöntemde oluşturulur . static
Daha sonra, yöntem olarak bildirilir public
.
class MyClass()
{
private:
MyClass() { }
public:
static MyClass * CreateInstance() { return new MyClass(); }
};
B. Sınıfınız bir singleton . Bu, programda sınıfınızın birden fazla örneğinin bulunmadığı anlamına gelir.
class MyClass()
{
private:
MyClass() { }
public:
MyClass & Instance()
{
static MyClass * aGlobalInst = new MyClass();
return *aGlobalInst;
}
};
C. (Yalnızca yaklaşan C ++ 0x standardı için geçerlidir) Birkaç kurucunuz var. Bazıları ilan edildi public
, diğerleri private
. Kod boyutunu küçültmek için, kamu kurucuları özel kurucuları çağırır ve bu da tüm işleri yapar. Bu public
nedenle kurucularınıza temsilci kurucular denir :
class MyClass
{
public:
MyClass() : MyClass(2010, 1, 1) { }
private:
MyClass(int theYear, int theMonth, int theDay) { /* do real work */ }
};
D. Nesne kopyalamayı sınırlamak istiyorsunuz (örneğin, paylaşılan bir kaynak kullandığından):
class MyClass
{
SharedResource * myResource;
private:
MyClass(const MyClass & theOriginal) { }
};
E. Sınıfınız bir yardımcı sınıftır . Bu sadece static
üyeler içerdiği anlamına gelir . Bu durumda, programda hiçbir nesne örneği oluşturulmamalıdır.
Başka bir arkadaş sınıfının / fonksiyonunun bir nesneyi kullanıcının yasakladığı şekilde inşa etmesini sağlayan bir "arka kapı" bırakmak. Akla gelen bir örnek, bir yineleyici (C ++) oluşturan bir kap olabilir:
Iterator Container::begin() { return Iterator(this->beginPtr_); }
// Iterator(pointer_type p) constructor is private,
// and Container is a friend of Iterator.
friend
garanti edilmediği yerlerde kullanabilen deneyimsiz C ++ programcılarını hedefliyor gibi görünüyor ve bunun kötü bir fikir olduğu birçok durum var. Ne yazık ki, mesaj çok iyi karşılandı ve birçok geliştirici dili asla ara sıra kullanımının friend
sadece kabul edilebilir değil, aynı zamanda tercih edilebilir olduğunu anlayacak kadar iyi öğrenmiyor . Örneğin sadece böyle bir durumdu. Kasıtlı güçlü bağlantı bir suç değildir, bir tasarım kararıdır.
Herkes Singleton'a takıldı, vay canına.
Diğer şeyler:
Bu, ortak kod içeren bir kurucu için çok yararlı olabilir; özel kurucular 'this (...);' kullanarak diğer kurucular tarafından çağrılabilir. notasyonu. Özel (veya korumalı) bir kurucuda ortak başlatma kodunu yaparak, yalnızca inşaat sırasında çağrıldığını açıkça açıkça belirtiyorsunuz, bu sadece bir yöntem olsaydı değil:
public class Point {
public Point() {
this(0,0); // call common constructor
}
private Point(int x,int y) {
m_x = x; m_y = y;
}
};
Genel bir kurucu kullanmak istemeyebileceğiniz bazı durumlar vardır; örneğin tek bir sınıf istiyorsanız.
3. taraflarca kullanılan bir derleme yazıyorsanız, yalnızca derlemeniz tarafından oluşturulmasını istediğiniz ve derlemenizin kullanıcıları tarafından başlatılmasını istemediğiniz birkaç dahili sınıf olabilir.
Bu, sizin (özel yapıcılı sınıf) yapıcıyı nasıl çağırdığınızı kontrol etmenizi sağlar.
Bir örnek: Sınıftaki statik fabrika yöntemi, fabrika yöntemi bunları ayırmayı seçtikçe nesneleri döndürebilir (örneğin, tek bir fabrika gibi).
Nesnenin yalnızca belirli bir sınıf tarafından oluşturulmasını önlemek için özel yapıcıya da sahip olabiliriz (Güvenlik nedeniyle).
C ++ örneği:
class ClientClass;
class SecureClass
{
private:
SecureClass(); // Constructor is private.
friend class ClientClass; // All methods in
//ClientClass have access to private
// & protected methods of SecureClass.
};
class ClientClass
{
public:
ClientClass();
SecureClass* CreateSecureClass()
{
return (new SecureClass()); // we can access
// constructor of
// SecureClass as
// ClientClass is friend
// of SecureClass.
}
};
Not: Not: Yalnızca ClientClass (SecureClass'ın arkadaşı olduğu için) SecureClass'ın Yapıcısını çağırabilir.
Özel kurucunun bazı kullanımları şunlardır:
Sizden de aynı sorunu ele alan bir soru gördüm.
Diğerlerinin örnek oluşturmasına izin vermek istemiyorsanız, yapıyı sınırlı bir kapsamda tutun. Pratik uygulama (Bir örnek) singleton örüntüsüdür.
Sen olmamalıdır yapıcı özel hale gelir. Dönemi. Korumalı hale getirin, böylece gerekirse sınıfı genişletebilirsiniz.
Edit: Ben bunun yanında, ne kadar downvotes bu atmak olursa olsun. Kod üzerinde gelecekteki geliştirme potansiyelini ortadan kaldırıyorsunuz. Diğer kullanıcılar veya programcılar sınıfı genişletmeye gerçekten kararlıysa, yapıcıyı kaynakta veya bayt kodunda korumalı olarak değiştirir. Hayatlarını biraz daha zorlaştırmanın dışında hiçbir şey başaramayacaksınız. Yapımcınızın yorumlarına bir uyarı ekleyin ve bunu ona bırakın.
Eğer bir yardımcı sınıf ise, daha basit, daha doğru ve daha zarif bir çözüm, uzatmayı önlemek için tüm sınıfı "statik final" olarak işaretlemektir. Sadece yapıcıyı özel olarak işaretlemek iyi olmaz; gerçekten kararlı bir kullanıcı yapıcıyı elde etmek için her zaman yansımayı kullanabilir.
protected
örneklemeyi sınırlamanıza veya pahalı kaynakları (DB bağlantıları, yerel kaynaklar) yeniden kullanmanıza izin veren statik fabrika yöntemlerini kullanmaya zorlamaktır.Yapıcı, singleton uygulamanız veya bir sınıfın nesne sayısını sınırlamanız gerektiği gibi bir amaçla özeldir. Örneğin singleton uygulamasında yapıcıyı özel yapmalıyız
#include<iostream>
using namespace std;
class singletonClass
{
static int i;
static singletonClass* instance;
public:
static singletonClass* createInstance()
{
if(i==0)
{
instance =new singletonClass;
i=1;
}
return instance;
}
void test()
{
cout<<"successfully created instance";
}
};
int singletonClass::i=0;
singletonClass* singletonClass::instance=NULL;
int main()
{
singletonClass *temp=singletonClass::createInstance();//////return instance!!!
temp->test();
}
Nesne oluşturma işlemini 10 ile sınırlamak istiyorsanız, aşağıdakileri kullanın
#include<iostream>
using namespace std;
class singletonClass
{
static int i;
static singletonClass* instance;
public:
static singletonClass* createInstance()
{
if(i<10)
{
instance =new singletonClass;
i++;
cout<<"created";
}
return instance;
}
};
int singletonClass::i=0;
singletonClass* singletonClass::instance=NULL;
int main()
{
singletonClass *temp=singletonClass::createInstance();//return an instance
singletonClass *temp1=singletonClass::createInstance();///return another instance
}
Teşekkürler
Birden fazla kurucunuz olabilir. Açık bir şekilde sağlamazsanız, C ++ varsayılan bir kurucu ve varsayılan bir kopya kurucu sağlar. Yalnızca bazı parametreli kurucular kullanılarak oluşturulabilecek bir sınıfınız olduğunu varsayalım. Belki değişkenleri başlattı. Bir kullanıcı bu sınıfı bu kurucu olmadan kullanırsa, sorunların sonu gelmez. İyi bir genel kural: Varsayılan uygulama geçerli değilse, hem varsayılan hem de kopya yapıcısını özel yapın ve bir uygulama sağlayın:
class C
{
public:
C(int x);
private:
C();
C(const C &);
};
Kullanıcıların nesneyi geçerli olmayan varsayılan kurucularla kullanmasını önlemek için derleyiciyi kullanın.
Etkili Java'dan alıntı yaparak , sabitleri (statik son alanlar olarak) tanımlayan bir yardımcı program sınıfına sahip olmak için özel yapıcıya sahip bir sınıfınız olabilir.
( DÜZENLEME: Yoruma göre bu sadece Java ile uygulanabilecek bir şeydir, bu yapının diğer OO dillerinde uygulanabilir / gerekli olup olmadığını bilmiyorum (C ++ diyelim))
Aşağıdaki gibi bir örnek:
public class Constants {
private Contants():
public static final int ADDRESS_UNIT = 32;
...
}
EDIT_1 : Yine, aşağıdaki açıklama Java için geçerlidir: (ve Etkili Java kitabından bahsederek )
Zararlı olmasa da, aşağıdaki gibi bir faydalı sınıf örneğinin oluşturulması, herhangi bir amaca hizmet etmediği için herhangi bir amaca hizmet etmez.
Örneğin, sınıf Sabitleri için özel bir Oluşturucu olmadığını varsayalım. Aşağıdaki gibi bir kod parçası geçerlidir, ancak Constants sınıfı kullanıcısının niyetini daha iyi iletmez
unit = (this.length)/new Constants().ADDRESS_UNIT;
kodun aksine
unit = (this.length)/Constants.ADDRESS_UNIT;
Ayrıca özel bir kurucu, Sabitler (diyelim) sınıfının tasarımcısının niyetini daha iyi ilettiğini düşünüyorum .
Herhangi bir kurucu sağlanmadıysa ve amacınız somutlaştırmayı önlemekse, Java varsayılan bir parametresiz genel kurucu sağlar.
Üst düzey bir sınıf statik işaretlenemez ve son sınıf bile örneklenebilir.
Fayda sınıflarının özel kurucuları olabilir. Sınıf kullanıcıları bu sınıfları başlatamaz:
public final class UtilityClass {
private UtilityClass() {}
public static utilityMethod1() {
...
}
}
Önemli kullanımlardan biri SingleTon sınıfında
class Person
{
private Person()
{
//Its private, Hense cannot be Instantiated
}
public static Person GetInstance()
{
//return new instance of Person
// In here I will be able to access private constructor
}
};
Sınıfınız sadece statik yöntemlere sahipse de uygundur. yani kimsenin sınıfınızı başlatmasına gerek yok
Bu gerçekten bariz bir neden: bir nesne oluşturmak istiyorsunuz, ancak bunu yapıcı içinde (arayüz açısından) yapmak pratik değil.
Factory
Örnek oldukça açıktır, beni göstermek izin Named Constructor
deyim.
Diyelim ki Complex
karmaşık bir sayıyı temsil edebilecek bir sınıfım var .
class Complex { public: Complex(double,double); .... };
Soru şudur: Kurucu gerçek ve hayali parçaları mı bekliyor, yoksa norm ve açıyı mı (kutupsal koordinatlar) bekliyor?
Arayüzü daha kolay hale getirmek için değiştirebilirim:
class Complex
{
public:
static Complex Regular(double, double = 0.0f);
static Complex Polar(double, double = 0.0f);
private:
Complex(double, double);
}; // class Complex
Buna Named Constructor
deyim denir : sınıf yalnızca hangi kurucuyu kullanmak istediğimizi açıkça belirterek sıfırdan oluşturulabilir.
Birçok inşaat yönteminin özel bir durumudur. Tasarım Desenleri inşa nesnesine yollardan iyi bir numarası: Builder
, Factory
, Abstract Factory
, ... ve özel bir yapıcı kullanıcı düzgün kısıtlanır sağlayacaktır.
Daha iyi bilinen kullanımlara ek olarak…
Ben şöyle özetlemek istiyorum Yöntem Nesne desenini uygulamak için :
“Özel kurucu, genel statik yöntem”
“Uygulama nesnesi, arayüz işlevi”
Nesneyi kullanarak bir işlev uygulamak istiyorsanız ve nesne bir kerelik bir hesaplama (yöntem çağrısı ile) yapmanın dışında kullanışlı değilse, bir Throwaway Nesneniz vardır . Nesne oluşturma ve yöntem çağrısını statik bir yöntemle kuşatabilir ve bu yaygın anti-paterni önleyebilirsiniz:
z = new A(x,y).call();
… Yerine (ad boşluklu) işlev çağrısı konulması:
z = A.f(x,y);
Arayanın asla bir nesneyi dahili olarak kullandığınızı, daha temiz bir arayüz sağladığını ve nesnenin etrafında asılı kalmasını veya nesnenin yanlış kullanımını önlediğini bilmesine ya da önemsemesine gerek yoktur.
Örneğin, yöntemlerin genelinde bir hesaplama kırmak istiyorsanız foo
, bar
ve zork
ve işlevleri dışında, bunu uygulamak olabilir aşağıdaki gibi birçok değerler geçirmek zorunda kalmadan hisse durumuna örneğin:
class A {
public static Z f(x, y) {
A a = new A(x, y);
a.foo();
a.bar();
return a.zork();
}
private A(X x, Y y) { /* ... */ };
}
Bu Yöntem Nesne kalıbı, Smalltalk Best Practice Patterns (Kent Beck, Kent Beck, sayfa 34–37) 'de verilmiştir;
- Orijinal yöntemi, yeni yöntemin bir örneğini oluşturan, özgün yöntemin parametreleri ve alıcısıyla oluşturulmuş ve "compute" işlevini çağıran bir yöntemle değiştirin.
Bu, buradaki diğer örneklerden önemli ölçüde farklıdır: sınıf somutlaştırılabilir (bir yardımcı sınıftan farklı olarak), ancak örnekler özeldir (tektonlar dahil fabrika yöntemlerinin aksine) ve asla kaçmadıkları için yığın üzerinde yaşayabilirler.
Bu model, nesnelerin düşük düzeyli uygulamayı basitleştirmek için kullanıldığı, ancak harici olarak açıkta bırakılmadığı alt dipteki OOP'de çok yararlıdır ve genellikle sunulan ve yüksek seviye arayüzlerle başlayan yukarıdan aşağı OOP ile tezat oluşturur.
Bir nesnenin örneklerinin nasıl ve ne zaman (ve kaç tane) oluşturulacağını kontrol etmek istiyorsanız bazen yararlı olabilir.
Diğerleri arasında, desenlerde kullanılır:
Singleton pattern
Builder pattern
Özel kurucuların kullanımı da alan güdümlü tasarım karşısında okunabilirliği / sürdürülebilirliği artırmak olabilir. "Microsoft .NET - Kurumsal Mimari Uygulamaları, 2. Baskı":
var request = new OrderRequest(1234);
Alıntı, "Burada iki sorun var. Birincisi, koda bakarken, neler olup bittiğini tahmin etmek zor. OrderRequest örneği yaratılıyor, ama neden ve hangi verileri kullanıyor? 1234 nedir? Bu ikinci soruna yol açar: Sınırlı bağlamın her yerde bulunan dilini ihlal ediyorsunuz: Dil muhtemelen böyle bir şey söylüyor: bir müşteri sipariş isteği verebilir ve bir satın alma kimliği belirlemesine izin verilir.Öyleyse, yeni bir OrderRequest örneği almanın daha iyi bir yolu var :"
var request = OrderRequest.CreateForCustomer(1234);
nerede
private OrderRequest() { ... }
public OrderRequest CreateForCustomer (int customerId)
{
var request = new OrderRequest();
...
return request;
}
Bunu her sınıf için savunmuyorum, ancak yukarıdaki DDD senaryosu için, yeni bir nesnenin doğrudan oluşturulmasını önlemenin mükemmel bir mantıklı olduğunu düşünüyorum.