Zaten tanıdığınız en azını söylemek biraz ezoteriktir ki, kodunuzu ilk kez karşılaştığınız zaman ne yaptığınızı ve bu yardımcı sınıfların tarzınızı almaya başlayana kadar nerede uygulandığını merak ettiğimde başımı kaşıma neden olabilir. / alışkanlıkları (hangi noktada buna tamamen alışırım).
Başlıklardaki bilgi miktarını azalttığınızı seviyorum. Özellikle çok büyük kod tabanlarında, derleme zamanı bağımlılıklarını azaltmak ve sonuçta inşa sürelerini azaltmak için pratik etkileri olabilir.
Bağırsak tepkime, uygulama ayrıntılarını bu şekilde gizlemeye ihtiyaç duyduğunuzda, kaynak dosyada dahili bağlantı ile bağımsız işlevlere geçen parametreyi tercih etmenizdir. Genellikle, belirli bir sınıfı uygulamak için sınıfın tüm iç bileşenlerine erişmeden yararlı işlevler (veya tüm sınıflar) uygulayabilir ve bunun yerine işlevin (veya yapıcı) bir yöntemin uygulanmasından ilgili olanları iletebilirsiniz. Ve doğal olarak bu, sınıfınız ve "yardımcılar" arasındaki bağlantıyı azaltma bonusuna sahiptir. Ayrıca, birden fazla sınıf uygulaması için daha genel bir amaca hizmet etmeye başladıklarını fark ederseniz, başka türlü "yardımcı" olabilecekleri genelleme eğilimi vardır.
Ben de kod "yardımcıları" bir sürü gördüğümde bazen biraz saçma. Bu her zaman doğru değildir, ancak bazen, kod çoğaltmasını ortadan kaldırmak için sadece işlevlerini willy-nilly olarak ayrıştıran bir geliştiricinin semptomatik olabilirler. diğer bazı işlevleri uygulamak için gereken kod. Sadece biraz daha ufacık düşünülmüş, bazen bir sınıfın uygulanmasının nasıl daha fazla fonksiyona ayrıştırıldığı ve nesnelerinizin tüm örneklerini iç kısımlara tam erişim ile geçmesine nasıl yardımcı olacağı konusunda çok daha fazla netliğe yol açabilir. tasarım düşünce tarzını teşvik etmek. Bunu yaptığınızı önermiyorum, elbette (hiçbir fikrim yok),
Bu uygunsuz olursa, pimpl olan ikinci, daha deyimsel bir çözüm düşünürdüm (bununla ilgili sorunlardan bahsettiğinizi anlıyorum, ancak minimum çaba göstermeyenlerden kaçınmak için bir çözümü genelleştirebileceğinizi düşünüyorum). Bu, özel verileri de dahil olmak üzere, sınıfınızın uygulanması gereken birçok bilgiyi toptancıdan uzaklaştırabilir. Pimpl'in performans sorunları, tam anlamıyla kullanıcı tanımlı kopya ctor uygulamak zorunda kalmadan değer semantiklerini korurken, ücretsiz bir liste gibi kir ucuz bir sabit zamanlı ayırıcı * ile hafifletilebilir.
- Performans yönü için pimpl en azından bir işaretçi yükü getiriyor, ancak pratik bir endişe teşkil eden durumlarda vakaların oldukça ciddi olması gerektiğini düşünüyorum. Mekansal konum, ayırıcı aracılığıyla önemli ölçüde bozulmazsa, nesne üzerinde yinelenen sıkı döngüler (eğer performans endişe verici ise, genellikle homojen olmalıdır), pratikte önbellek özlemlerini en aza indirgeme eğilimindedir. pimpl'i tahsis etmek için ücretsiz bir liste, sınıfın alanlarını büyük ölçüde bitişik bellek bloklarına koyarak.
Şahsen sadece bu olasılıkları tükettikten sonra böyle bir şey düşünürdüm. Alternatifin, belki de sadece ezoterik doğasının pratik endişe olduğu başlığa maruz kalan daha özel yöntemler gibi olması iyi bir fikirdir.
Bir alternatif
Hemen hemen aynı amaçlara ulaşan aklıma gelen bir alternatif, arkadaş yokluğunda şöyle:
struct PredicateListData
{
int somePrivateField;
};
class PredicateList
{
PredicateListData data;
public:
bool match() const;
};
// In source file:
static bool fullMatch(const PredicateListData& p)
{
// Can access p.somePrivateField here.
}
bool PredicateList::match() const
{
return fullMatch(data);
}
Şimdi bu çok tartışmalı bir fark gibi görünebilir ve ben yine de ona "yardımcı" diyorum (muhtemelen bütünüyle inatçı bir anlamda, sınıfın tüm iç durumunu, hepsine ihtiyacı olsun ya da olmasın, fonksiyona geçiriyoruz) ancak "şok" karşılaşma faktörünü engellemez friend
. Genel olarak friend
, daha fazla denetim yapılmadığını görmek biraz korkutucu görünüyor, çünkü sınıf içi araçlarınıza başka bir yerden erişilebildiğini söylüyor (bu da kendi değişmezlerini koruyamayabileceği anlamına geliyor). Kullandığınız yolla, friend
insanlar uygulamanın farkında olduğundan,friend
sadece sınıfın özel işlevselliğinin uygulanmasına yardımcı olan aynı kaynak dosyada ikamet ediyor, ancak yukarıdakiler, en azından bu türden kaçınan herhangi bir arkadaş içermemesi muhtemel tartışmalı bir fayda ile aynı etkiyi gerçekleştirir ("Oh ateş, bu sınıfın bir arkadaşı var. Başka nerelere erişiyor / mutasyona uğruyor? "). Oysa hemen yukarıdaki sürüm derhal uygulamada yapılan herhangi bir şeyin dışında haklara erişilmesinin / mutasyon geçirilmesinin bir yolu olmadığını bildirir PredicateList
.
Bu belki de bu nüans seviyesiyle biraz dogmatik bölgelere doğru ilerliyor, çünkü herkes bir şeyleri aynı şekilde adlandırır *Helper*
ve hepsini bir sınıfın özel uygulamasının bir parçası olarak bir araya getirildiği aynı kaynak dosyasına koyabilir. Ancak nit-seçici alırsak, belki de hemen üstteki stil, bir bakışta diz sarsıntısı reaksiyonuna neden olmaz, friend
bu da biraz korkutucu görünme eğiliminde olan anahtar kelimeyi içermez.
Diğer sorular için:
Bir tüketici kendi PredicateList_HelperFunctions sınıfını tanımlayabilir ve özel alanlara erişmesine izin verebilir. Bunu büyük bir sorun olarak görmeme rağmen (gerçekten bu özel alanlarda istersen biraz döküm yapabilirsin), belki de tüketicileri bu şekilde kullanmaya teşvik eder?
Bu, istemcinin aynı ada sahip ikinci bir sınıf tanımlayabileceği ve bağlantı hatalarına neden olmadan iç kısımlara bu şekilde erişebileceği API sınırları boyunca bir olasılık olabilir. Sonra yine büyük ölçüde grafiklerde çalışan ve bu düzeydeki güvenlik kaygılarının öncelik listesinde çok düşük olduğu bir C kodlayıcıyım, bu yüzden bunlar gibi endişeler ellerimi sallayıp dans etme eğilimindeyim yokmuş gibi davranmaya çalışın. :-D Böyle endişelerin oldukça ciddi olduğu bir alanda çalışıyorsanız, bence bu iyi bir düşüncedir.
Yukarıdaki alternatif öneri de bu sorunu yaşamamaktadır. Yine de kullanmaya devam etmek istiyorsanız friend
, yardımcıyı özel iç içe bir sınıf haline getirerek de bu sorunu önleyebilirsiniz.
class PredicateList
{
...
// Declare nested class.
class Helper;
// Make it a friend.
friend class Helper;
public:
...
};
// In source file:
class PredicateList::Helper
{
...
};
Bu, adı olan iyi bilinen bir tasarım deseni mi?
Bildiğim kadarıyla yok. Uygulama detaylarının ve stilinin minutisine girdiği için bir tane olacağından şüpheliyim.
"Yardımcı Cehennem"
Birçok "yardımcı" kod içeren uygulamaları gördüğümde bazen nasıl küfür ettiğim hakkında daha fazla açıklama talebi aldım ve bu bazılarıyla biraz tartışmalı olabilir, ancak bazı hata ayıklama yaparken gerçekten küfür ettiğim gibi sadece bir sürü "yardımcı" bulmak için meslektaşlarımın bir sınıfı uygulaması. :-D Ve tüm bu yardımcıların tam olarak ne yapması gerektiğini anlamaya çalışan başımda kayan tek takım ben değildim. Ben de "yardımcıları kullanamayacaksınız" gibi dogmatik bir şekilde çıkmak istemiyorum , ama pratik olmadığında bunların bulunmadığı şeylerin nasıl uygulanacağını düşünmeye yardımcı olabileceğine dair ufak bir öneride bulunacağım.
Tüm özel üye işlevleri tanım gereği yardımcı işlevler değil mi?
Ve evet, özel yöntemler ekliyorum. Basit bir ortak arayüze sahip bir sınıf görürsem find_impl
ya find_detail
da ya da ya da amaç olarak biraz kötü tanımlanmış sonsuz bir özel yöntem seti gibi görürsem find_helper
, aynı şekilde benzer şekilde kandırırım.
Alternatif olarak önerdiğim static
, sınıfınızı "diğerlerinin uygulanmasına yardımcı olan bir işlevden" en az daha genel bir amaçla uygulamaya yardımcı olmak için dahili bağlantıya sahip (beyan edilen veya anonim bir ad alanı içinde) olmayan üye olmayan arkadaşlık işlevleridir . Ve burada genel bir SE açısından tercih edilebileceği için Herb Sutter'i neden C ++ 'Kodlama Standartları'ndan alıntılayabilirim:
Üyelik ücretlerinden kaçının: Mümkünse, üyesi olmayanları işlevsiz yapmayı tercih edin. [...] Üye olmayan arkadaş olmayan işlevler bağımlılıkları en aza indirerek kapsüllemeyi geliştirir: İşlevin gövdesi sınıfın kamuya açık olmayan üyelerine bağlı olamaz (bkz. Madde 11). Ayrıca, ayrılabilir işlevselliği serbest bırakmak ve birleşmeyi daha da azaltmak için monolitik sınıfları ayırırlar (bkz. Madde 33).
Değişken kapsamını daraltmanın temel ilkesi açısından bir dereceye kadar konuştuğu “üyelik aidatlarını” da anlayabilirsiniz. En uç örnek olarak, tüm programınızın çalışması için gereken tüm kodlara sahip bir Tanrı nesnesini hayal ederseniz, o zaman bu türden "yardımcıları" (üye fonksiyonlar veya arkadaşlar olsun) tüm içsellere ( sınıfın temelleri) temel olarak bu değişkenleri genel değişkenlerden daha az problemli hale getirmez. Bu en uç örnekte durumu doğru yönetmek ve güvenliği sağlamak ve global değişkenlerle elde edeceğiniz değişmezleri korumak için tüm zorluklara sahipsiniz. Ve elbette çoğu gerçek örnek umarım bu uç noktaya yakın bir yerde değildir, ancak bilgi gizleme sadece erişilen bilginin kapsamını sınırladığı kadar yararlıdır.
Şimdi Sutter burada zaten güzel bir açıklama yapıyor, ancak ayrıştırma işlevlerini nasıl tasarladığınız açısından psikolojik bir gelişme (en azından beyniniz benim gibi çalışıyorsa) gibi gelişmeye meyilli. Yalnızca geçtiğiniz ilgili parametreler dışında sınıftaki her şeye erişemeyen işlevler tasarlamaya başladığınızda veya sınıf örneğini bir parametre olarak iletirseniz, yalnızca genel üyeleri, tercih eden bir tasarım zihniyetini teşvik etme eğilimindedir. ayırma işleminin üstünde ve gelişmiş kapsüllemeyi teşvik eden, her şeye erişebiliyorsanız, tasarım yapmak için cazip olabileceğinizden daha net bir amaca sahip işlevler.
Ekstremitelere geri dönersek, küresel değişkenlerle dolu bir kod tabanı, geliştiricileri, işlevleri açık ve genelleştirilmiş bir şekilde tasarlamaya tam olarak teşvik etmez. Çok hızlı bir şekilde bir fonksiyonda ne kadar çok bilgiye erişirseniz, fanilerimiz dejenere dönüşme ve o fonksiyona daha spesifik ve ilgili parametreleri kabul etmek yerine sahip olduğumuz tüm bu ekstra bilgilere erişme lehine netliğini azaltma cazibesi ile karşı karşıya kalıyoruz. devlete erişimini daraltmak ve uygulanabilirliğini genişletmek ve niyet netliğini arttırmak. Bu, üye işlevler veya arkadaşlarla (genellikle daha az derecede olsa da) geçerlidir.