Doktor Brown'un cevabı , Demeter Hukukunun klasik bir ders kitabı uygulamasını gösteriyor - ve bu şekilde onlarca yöntem eklemenin can sıkıntısı / düzensiz kod bloğu, muhtemelen kendileri de dahil olmak üzere programcıların sık sık bunu yapmak zorunda kalmasalar bile zahmet etmiyor olmalarıdır.
Nesnelerin hiyerarşisini ayırmanın alternatif bir yolu var:
Yöntemleriniz ve özellikleriniz aracılığıyla türler interfaceyerine türleri gösterin class.
Orijinal Poster'in (OP) durumunda, yerine a değerini encoder->WaitEncoderFrame()döndürür ve hangi işlemlere izin verileceğini tanımlar.IEncoderFrameFrame
ÇÖZÜM 1
En kolay durumda Frameve Encodersınıflar her ikisi de sizin kontrolünüz altındadır, IEncoderFrameFrame zaten kamuya açık bir şekilde ortaya konan yöntemlerin bir alt kümesidir ve Encodersınıf aslında o nesneye ne yaptığınızla ilgilenmez. O zaman, uygulama önemsizdir ( c # kodu ):
interface IEncoderFrame {
void DoOrGetSomething();
}
class Frame : IEncoderFrame {
// A method that already exists in Frame.
public void DoOrGetSomething() { ... }
}
class Encoder {
private Frame _frame;
public IEncoderFrame TheFrame { get { return _frame; } }
...
}
ÇÖZÜM 2
FrameTanımın kontrolünüz altında olmadığı veya IEncoderFrameyöntemlerin eklenmesinin uygun olmadığı bir orta durumda, o Framezaman iyi bir çözüm bir Adaptördür . Yani ne CandiedOrange cevabı olarak, adı geçen new FrameHandler( frame ). ÖNEMLİ: Bunu yaparsanız , bir sınıf olarak değil, bir arabirim olarak göstermeniz daha esnektir . bilmek zorunda , ama müşterilerin sadece bilmesi gerekiyor . Ya da adlandırdığım gibi, - Kodlayıcının POV'sinden görüldüğü gibi özel olarak Frame olduğunu belirtmek için :Encoderclass FrameHandlerinterface IFrameHandlerinterface IEncoderFrame
interface IEncoderFrame {
void DoOrGetSomething();
}
// Adapter pattern. Appropriate if no access needed to Encoder.
class EncoderFrameWrapper : IEncoderFrame {
Frame _frame;
public EncoderFrameWrapper( Frame frame ) {
_frame = frame;
}
public void DoOrGetSomething() {
_frame....;
}
}
class Encoder {
private Frame _frame;
// Adapter pattern. Appropriate if no access needed to Encoder.
public IEncoderFrame TheFrame { get { return new EncoderFrameWrapper( _frame ); } }
...
}
MALİYET: Yeni bir nesnenin tahsisi ve GC'si, EncoderFrameWrapper, her zaman encoder.TheFrameçağrılır. (Bu sargıcıyı önbelleğe alabilirsiniz, ancak bu daha fazla kod ekler. Ayrıca, kodlayıcının kare alanı yeni bir kareyle değiştirilemezse yalnızca güvenilir bir şekilde kodlamak kolaydır.)
ÇÖZÜM 3
Daha zor durumda, yeni ambalajın hem Encoderve hem de hakkında bilmesi gerekir Frame. Bu nesnenin kendisi LoD'yi ihlal eder - Encoder ile Frame arasında, Encoder'ın sorumluluğunda olması gereken bir ilişkiyi manipüle ediyor - ve muhtemelen haklı olmak için acı verici. İşte o yola başlarsanız neler olabilir:
interface IEncoderFrame {
void DoOrGetSomething();
}
// *** You will end up regretting this. See next code snippet instead ***
class EncoderFrameWrapper : IEncoderFrame {
Encoder _owner;
Frame _frame;
public EncoderFrameWrapper( Encoder owner, Frame frame ) {
_owner = owner; _frame = frame;
}
public void DoOrGetSomething() {
_frame.DoOrGetSomething();
// Hmm, maybe this wrapper class should be nested inside Encoder...
_owner... some work inside owner; maybe should be owner-internal details ...
}
}
class Encoder {
private Frame _frame;
...
}
Bu çirkindi. Sargının yaratıcısının / sahibinin (Encoder) ayrıntılarına dokunması gerektiğinde daha az karmaşık bir uygulama vardır:
interface IEncoderFrame {
void DoOrGetSomething();
}
class Encoder : IEncoderFrame {
private Frame _frame;
// HA! Client gets to think of this as "the frame object",
// but its really me, intercepting it.
public IEncoderFrame TheFrame { get { return this; } }
// This is the method that the LoD approach suggests writing,
// except that we are exposing it only when the instance is accessed as an IEncoderFrame,
// to avoid extending Encoder's already large API surface.
public void IEncoderFrame.DoOrGetSomething() {
_frame.DoOrGetSomething();
... make some change within current Encoder instance ...
}
...
}
Kabul ediyorum, buraya geleceğimi bilseydim, bunu yapmayabilirdim. LoD yöntemlerini yazabilir ve onunla yapılabilir. Arayüz tanımlamanıza gerek yok. Öte yandan, arayüzün ilgili yöntemleri bir araya getirmesini seviyorum. Çerçeve gibi hissettiren "çerçeve benzeri işlemleri" yapmanın nasıl bir his olduğunu seviyorum.
SON YORUMLAR
Bunu göz önünde bulundurun: Maruz kalmanın keçe uygulayıcısı genel mimarisine uygunsa veya "LoD uygulamasından çok daha kolay" olsaydı, bunun yerine gösterdiğim ilk pasajı yaparlarsa çok daha güvenli olurdu - sınırlı bir alt kümesini ortaya çıkardı Arayüz olarak çerçeve. EncoderFrame frame Benim tecrübeme göre, bu genellikle tamamen uygulanabilir bir çözümdür. Arayüze gerekli yöntemleri ekleyin. (Frame'in zaten gerekli metotlara sahip olduğunu veya bilmemiz gereken tartışmasız olacağını bildiğimiz bir senaryodan bahsediyorum. Her yöntemin "uygulama" çalışması, arayüz tanımına bir satır ekliyor.) Ve burada, kaldırarak - hatta en kötü gelecek senaryosunda, API çalışmaya devam etmek mümkün olduğunu biliyoruz IEncoderFramedanFrameEncoder.
Ayrıca dikkat Eklemek izniniz yok eğer IEncoderFrameiçin Frameveya gerekli yöntemler genel iyi uymaz Frameçözüm 2. belki de ekstra nesne oluşturma-ve-imha, size uygun değildir sınıfı ve, çözüm # 3, EncoderLoD'yi başarmanın yöntemlerini düzenlemenin basit bir yolu olarak görülebilir . Sadece düzinelerce yöntemi geçmeyin. Bunları bir Arabirime sarın ve "açık arabirim uygulaması" (c # kullanıyorsanız) kullanın; böylece nesneye yalnızca bu arabirimden bakıldığında erişilebilirler.
Vurgulamak istediğim bir diğer nokta , işlevselliği bir arayüz olarak gösterme kararının yukarıda açıklanan durumların 3'ünü de ele almasıdır. İlki, IEncoderFramesadece Frameişlevselliğinin bir alt kümesidir . İkincisi, IEncoderFramebir adaptördür. Üçüncüsü, işlevselliğine IEncoderFramebir bölümdür Encoder. Gereksinimlerinizin bu üç durum arasında değişip değişmediği önemli değildir: API aynı kalır.