Jag har funderat lite kring Law of demeter och Collections i .NET. Det behöver inte vara fel att exponera underobjekt. Till exempel är det bättre att köra med Order.Customer.Name än att skapa ett attribut CustomerName i Order och skriva Order.CustomerName. Det känns ju mer "Dotnetmässigt" att skriva Jag håller med dig, det känns faktiskt som att det är dotnetmässigt. Men frågan är var man skall dra gränsen. Vad är dotnetmässigt? ;-) Kastar lite bensin på elden: >Rörande Order.Customer.Name Håller med dig principerna är inga lagar, men det är trevligt om man följer dom någorlunda för att få bättre kod. Så här skulle jag vilja uttrycka LoD: Pilla inte inne i ett objekt utifrån! Eller som kanske är mer känt: Exponera inte ett objekts inre struktur utåt. Jepp håller med om att Order.OrderLines.Add(orderLine); inte följer LoD, och för utvecklaren som använder Order så kanske är en Order.Add(product, price); är jättebra, men jag tror fortfarande man skall ha kvar order.OrderLines(orderline); för jag tror att en .net utvecklare förväntar sig den listan på Order. Roger: <b>Nu är du inte på abstraktionsnivåer gällande OO och OOP.</b> Per: Håller nog med Johan om att Niclas: Jag håller med dig fullständigt, tror vi missuppfattade varandra någonstans. <b>Per: Per. Vad som är lämpligt beror som alltid på sammanhanget, på vem som skall använda objektet. En bilist och en bilreparatör behöver olika gränssnitt till bilen. >>Nja. ROLS säger inget om att man skall skriva ClassA.CLassBs.Add... Problemet är ju att man vill skapa domänregler för hur man lägger till t.e.x en order. Standard interfacen har ju bara en möjlighet och det är ju att lägga till ett objekt av typen som listan har. För att sedan dessutom hantera LS så måste du ju vara säker på att listan du skapar är IEnumerable, åtminstone. >>Så frågan är, skall vi alltid slaviskt följa LOD eller ROLS? ROLS är f-ö det MS hävdar är problemet med aspekter ;) >>Nja. ROLS säger inget om att man skall skriva ClassA.CLassBs.Add... >Ang LOD. Det jag menar är bara att: ClassB är ingen temp variabel. Det är ett aggregat ur en Aggregat root ClassB. Law of demeter och .NET med Collections
Vad jag har fattat det som så bryter följande två rader mot Law of demeter.
Order.OrderLines.Add(orderLine);
Order.OrderLines.Count;
Jag antar att Law of demeter vill ha det såhär
Order.AddOrderline(orderLine); vilket jag köper.
Order.OrderLinesCount;
Order.GetOrderLineEnumerator();
Order.SortOrderItems();
Order.GetOrderItemBy...
Jag tycker att Order börjar bli lite väl stor och att den tar över för stor del av en eventuell OrderLineCollection visserligen så kommer logiken ligga i OrderLineCollection, men jag tycker att det blir mycket kod att underhålla för att visa den funktionalitet som finns i OrderLineCollection i Order.
Hur tänker ni i dessa fall?Sv: Law of demeter och .NET med Collections
Så eftersom du ändå i praktiken exponerar orderrader genom de metoder du placerar i Order, så kan du lika väl exponera hela kollektionen.Sv: Law of demeter och .NET med Collections
Order.OrderLines.Add(orderLine);
faktiskt!
Jämför med hur det är i Frameworket. T.ex:
ListBox.Items.Add(itm);
Klart man ska vara Dotnetmässigt i Dotnet,
då kommer ju nästa utvecklare i projektet må som fisken i vattnet.. :)Sv:Law of demeter och .NET med Collections
Men gör man en bra domänmodell och ett bra program behöver det kanske inte bli så mycket sådant.Sv: Law of demeter och .NET med Collections
Vaför är det dotnetmässigt? Jag vet flera som inte skriver så men allt för många som gör det, men varför? Jo för det är lätt och ingen har sagt åt oss att göra på annat sätt. Slå mig inte levande nu ;-) men ca 80% av .net utvecklare är faktiskt inte så bra, de är inte heller dåliga men inte närheten från att kunna anses som riktigt bra. Det finns så mkt common misstakes i koden där ute som man kan förhindra om man bara ansträngde sig mer i området Bra Kod. Det är där principer kommer in för att hjälpa oss så som ex Law of demeter (LoD) . Men jag säger inte att man måste följa den, men kommer nedan försöka förklara mer varför den finns där.
LoD handlar om att man inte skall göra en "Train Wreck" ex. Hur visar du i kod om en hund är glad? Jo man viftar på dess svans ex: dog.GetBody().GetTail().Wag
Detta är inte bra för att man går genom flera interface och objekt. Skulle vi köra TDD med mockning skulle detta leda till en hel del propblem. Då du går genom flera objekt som man kanske vill mocka bort och då blir mockning ett problem.
Dvs vad händer om du mockar bort Tail från Body i koden ovan?
En annan sak är att det även blir svårmodifierad kod. Vad gör raden egentligen? Vad säger den? En stor regel ang LoD ät "Tell, dont ask!" en regel som i of s gäller allt som handlar om bra kod.
Dog.ExpressHappnies() är mer rätt. Och det säger sig självt vad man faktiskt gör.
Dvs. Tell...
Medans dog.GetBody().GetTail().Wag säger inte så mkt... Varför vickar jag på svansen?
Dvs: I asked....
"Tell, Don't ask!"
Men vist det kan bli en del metoder. Men vad gör det så länge man förstår vad de gör? Det är värre att ha färre metoder med kod man inte förstår än tvärt om, samma med metodnamn, hellre ett långt förklarande namn än kort som inte säger något.
Order.OrderLines.Add(item)
Denna rad är ju lite självförklarade. Följer man principen KISS (Keep it simple stupid) så är denna rad inte så stupid. Order.AddOrderLineItem(item) är mer stupid och det är precis det man skall försöka uppnå.
Säg att OrderLines är en prop med List<Item> som sedan kanske refactoreras ut till en helt egen och annan klass än List där add metoden inte finns längre. Order.AddOrderLine(item) blir då den enda metod att uppdatera medans det finns risker med Order.OrderLines.Add(item), alla dessa måste pekas om och denna rad kod kan faktiskt finnas på flera ställen i applikationens kod samt i tester som man då måste skriva om.
Rörande Order.Customer.Name
Tanken här är inte att skapa en CustomerName på order utan att istället skriva:
Customer customer = Order.Customer;
string name = customer.Name;
Dvs 1 punkt...
En annan sak att tänka på är att Principer är inga lagar de finns där för hjälpa till. Jag personligen tycker LoD är en trevlig princip och den ökar stabilitet och ger ökad modifierbar och löskopplad kod samt mer självförklarande kod vilket är 3 grundstenar i bra kod.
Mvh JohanSv: Law of demeter och .NET med Collections
LoD bryter mot Rule of least surprise när den appliceras på .NET kod..
Man förväntar sig att man kan göra .Add .Remove etc på listor eftersom vi har ett standard kontrakt för listor i .NET (IList , IList of T)
Att bryta mot Rule of least surprise är en synd vid API design ;-)Sv:Law of demeter och .NET med Collections
>Tanken här är inte att skapa en CustomerName på order utan att istället skriva:
>Customer customer = Order.Customer;
>string name = customer.Name;
Njae...
från wikipedia:
"In particular, an object should avoid invoking methods of a member object returned by another method"
Customer är ett member object returnerat av en metod i order (get_Customer)
Din consumer kod som pratar med order får alltså inte anropa något på customer enligt LoD.
Så min tolkning är att du faktiskt visst ska ha en CustomerName på Order enlgit LoD.Sv:Law of demeter och .NET med Collections
Just min fråga var för att jag ville få det konstaterat, Collection.Add används ju t.ex. av Microsoft och de dom gjort i sin Framework design får väl man försöka härma eftersom det är deras design av klasser som de flesta .NET utvecklare idag använder och har anamat.Sv:Law of demeter och .NET med Collections
Exempel som bryter mot regeln: dog.Body.Tail.Wag()
Exempel som följer regeln: dog.ExpressHappiness()
Jag anser att varken Order.OrderLines.Add(orderLine) eller Order.AddOrderline(orderLine) verkligen följer LoD. Mer korrekt skulle vara Order.Add(product, price).
När det gäller Order.Customer finns det en viktig skillnad mot dog.Body: kunden är inte en del av ordern, medan kroppen är en del av hunden. Kunden är snarare ägare till ordern, så att returnera kunden innebär inte att man exponerar delar av ordern. Och namnet skall man nog inte heller kalla för en del av kunden; det är en etikett eller en egenskap hos kunden, inte ett objekt i egentlig betydelse (även om den kanske implementeras som ett objekt).Sv: Law of demeter och .NET med Collections
Precis som att Form har form.Controls.Add(control); form.AddControl(control);Sv:Law of demeter och .NET med Collections
>"LoD bryter mot Rule of least surprise när den appliceras på .NET kod.. "
Nja. ROLS säger inget om att man skall skriva ClassA.CLassBs.Add... Den säger ju bara att man skall mer eller mindre ha logiska interface och metoder så man slipper tänka så mkt. Dvs logiskt att Listor har Add, Remove, men det säger inget om att du skall bryta mot LoD eller skriva kod med flera kedjor.
>"In particular, an object should avoid invoking methods of a member object returned by another method"
Denna säger precis detta: ClassA.ClassB.Foo() <--- anropa inte foo på detta sätt.
Dvs ClassA retunterar ClassB och man skall inte anropa dess metod.
Men det finns inget som säger att du inte får hämta ClassB för att sen anropa dess metod.
Om så vart fallet skulle man inte kunna använda en hög Design Mönster. Lika så OO/OOP reglerna som i princip säger att man skall inte gå mot ett aggregat direkt utan hämta det från aggregat rooten (ägaren) för att sen använda aggregatet.
Per:
>"Jag anser att varken Order.OrderLines.Add(orderLine) eller Order.AddOrderline(orderLine) verkligen följer LoD. Mer korrekt skulle vara Order.Add(product, price). "
Nu är du inne på abstraktionsnivåer gällande OO och OOP.
LoD säger bara att du inte bör skriva Order.OrderLines.Add(orderLine) utan hellre lägga metod på Order som gör detta åt en.
Parantes (hör inte hit egentligen):
Jag tycker inte Order.Add(product, price) är bättre. Jag skulle nog hellre (om man skall vara petig) gjort:
Order.AddProduct(product, quantity) för läser man Order.Add så blir det mer i stil med att man vill lägga till en order men det är ju en produkt jag lägger till. En produkt har oftast ett pris så det vill jag inte lägga in, dock vill jag lägga in antal. Men detta är en annan diskussion, ville bara va petig här... Poängen är att LoD är inte OO.
Niclas:
>"för jag tror att en .net utvecklare förväntar sig den listan på Order. "
Nja. Det är lite detta som är fel anser jag. Det finns inget som heter, ".net utvecklare förväntar sig"
Jag förväntar mig det inte. Jag förväntar mig löskopplad kod, modifierbar kod, läsbar kod som beskriver mina krav och skapar en berättande kod för mig. Implementationen är det som ger mig svar på vad applikationen/koden gör och det är det jag förväntar mig.
Mvh JohanSv: Law of demeter och .NET med Collections
So what? Ofta behövs beskrivande exempel.
<b>Jag tycker inte Order.Add(product, price) är bättre.</b>
Jag är färgad av nuvarande jobbet där det knappt finns anledning att köpa flera av samma produkt. Däremot kan man få rabatt om man är med i en klubb.Sv:Law of demeter och .NET med Collections
Till vem savara du? ang:
"<b>Nu är du inte på abstraktionsnivåer gällande OO och OOP.</b>
So what? Ofta behövs beskrivande exempel.
<b>Jag tycker inte Order.Add(product, price) är bättre.</b>
Jag är färgad av nuvarande jobbet där det knappt finns anledning att köpa flera av samma produkt. Däremot kan man få rabatt om man är med i en klubb."Sv:Law of demeter och .NET med Collections
Order.Add(product, quantity);
Möjligtvis isåfall om man skulle ha en extra Add med .Add(product, quantity, price); vid det specialfall du ger.
Däremot så tycker jag fortfarande att man bör följa mönstret som .NET Framework byggt upp som t.ex. det exempel jag gav med Forms.Controls. Dock så håller jag ju med att man skall ha en löst kopplad design men det är just när det gäller collection's som jag på något sätt känner att det hör till objektet men där man ändå separerat logiken.
Vi får ta upp detta vid nästa codecamp Johan ^^Sv: Law of demeter och .NET med Collections
Rörande: Collection.Add
I detta fall bryter det ju inte mot LoD då du har en punkt. Sen så är det ju ganska logiskt att en samling kan lägga till nått. Samma som med List.add i detta fall vet man att man lägger till nått i en lista.
Detta är bla det Roger tog upp rörande Rule of least surprise.
Listor, Collections etc har vissa förväntade APIer.
Ta Order, OrderLines som exempel. Här förväntar jag mig att OrderLines som troligen är en collection/lista av något som har metoder som Add och Remove medans Order har det inte. Men för att inte bryta LoD så lägger man mer eller mindre koden ”order.OrderLine.Add(...)” i en metod på sin Order istället som förklarar vad man gör.
typ:
public class Order
{
public void AddProduct(....)
{
_orderLine.Add(....);
}
}
Sen vet jag att MS bla gör så här i sina exempel:
Panel Panel1= new Panel();
Panel1.Controls.Add(myLabel);
MS exempel säger inte att så här skall man göra utan visar hur man kan göra.
Alltså de visar bara enklaste möjliga exempel, som inte följer någon best practices.
Det är tyvärr detta som är lite synd. Detta har de insett och på flera ställen lagt till varningsruta som säger att koden visar bara hur man kan göra inte så här skall man göra. Om MS börjar stå för massa principer så rekommenderar de saker som är upp till en själv att välja. Så de gör helt rätt, problemet är dock att många utvecklare inte är observanta ang detta och kör gärna ripp-off av deras exempel och tror att så skall man koda.
Ang ovan exempel så hade jag dock gärna sett en metod på panel som hette AddControl.
Vet att många ärver en del controller för att lägga till sina mer logiska metoder för att inte bryta LoD, tack vare extensionmetoder blir det to m lättare.
Det är ju så att .net ramverk är låst. Du kan inte modifiera .Net framwork och på så sätt kan man leva lite på Rule of least surprise. Men din kod är tyvärr den kod som kommer att förändras och den enda kod du kan förändra så där är det viktigare att lägga ner energi på att få till bra kod.
Mvh JohanSv:Law of demeter och .NET med Collections
Ne allt som Microsoft gör tycker jag inte är bra t.ex. DataSet's.
Jag tycker att jag har fått en ganska bra bild och ett bra svar på min fråga så jag stänger diskussionen med detta.
Tack för alla svar och synpunkter.Sv: Law of demeter och .NET med Collections
Till vem savara du?</b>
Det var till dig, Johan.
<b>Rörande: Collection.Add
I detta fall bryter det ju inte mot LoD då du har en punkt.</b>
Antalet.punkter.är.knappast.avgörande....... Inte går man från att bryta mot LoD till att följa den bara för att man byter ut dog.Body.Tail.Wag() mot
dog_body = dog.Body;
dog_tail = dog_body.Tail;
dog_tail.Wag();Sv:Law of demeter och .NET med Collections
Sorry hehe. såg först nu att jag skrev "inte" det skulle vart "inne" meningen skulle vara:
Nu är du inne på abstraktionsnivåer gällande OO och OOP.
Ang:
dog_body = dog.Body;
dog_tail = dog_body.Tail;
dog_tail.Wag();
Nu är det ju lite udda exempel du tar upp här då man säkerligen vill göra nått med denna kod. Ev ligger denna kod i just Dog.ExpressHappiness() fast då utan dog_body = dog.Body;
Eller så har man service klasser, factories etc... som vill göra nått med de subtyper som finns inne i en hierarki.
Ex. Säg att du skall plocka ut en lampa från en bildörr (såndär sidolampa som visar att dörren är öppen.)
Då kan du mkt väl göra (OBS! en Service klass som skall göra nått här):
private Lamp GetWarningLampFromCar(Car car)
{
Door door = car.Door;
return door.WarningLamp;
}
OBS! - OO-service, inte SOA, eller MCF service.
I detta fall vill man inte ha Door.GetLeftDoorWarningLamp() om det inte är en vanlig förekomst.
Så regeln säger bara att man inte skall ha mer än 1 punkt och att man inte skall gå via en klass metod som returnerar en annan klass för att anropa dess metod. Men inget säger att du inte får anropa metoden som ger en klass för att sen på annan rad faktiskt använda den.
Nu finitar vi denna tråden... Kul diskussion... Många tankar. kuliga idéer... Och lite för mkt implementationsdetaljer nu för case som är svåra att beskriva...
Mvh JohanSv: Law of demeter och .NET med Collections
En sak håller jag klart med om iaf: många punkter är en varningsklocka. Ju fler punkter, desto större risk att det bryter mot god OO och då främst mot LoD.Sv: Law of demeter och .NET med Collections
Jo, ROLS säger att du ska använda det sätt som gör användaren minst förvånad.
Eftersom vi har intefaces / kontrakt i .NET som är till för listor så bryter det mot ROLS att inte använda dessa interfaces.
Om något är i listform så förväntas man använda IList eller IList of T.
Eller i andra hand, arrayer.
Om du skulle införa ett eget interface "IJohanList" som gör exakt samma sak fast utan Add och Remove så skulle nog de flesta klia sig i huvudet och undra varför.
och om det sker, så har du brutit mot ROLS.
>> Men det finns inget som säger att du inte får hämta ClassB för att sen anropa dess metod.
Att mellanlagra objektet i en tmp variabel gör inte ditt program mindre buggfritt på något sätt.
På vilket sätt menar du att du får en mindre buggig applikation genom att mellanlagra resultatet av ett metodanrop i tmp variabler utan att tillföra några conditions på dessa?
I runtime så är det ju _exakt_ samma kod som exekverar om du kompilerar i releaseläge.
Så jag antar att du menar att man får någon form av positiv effekt av det hela medans man tittar på sourcekoden?
isåfall, vilken?Sv:Law of demeter och .NET med Collections
Det innebär att du får skapa egna listtyper, något i stil med:
public class OrderLine
{
public int Id { get; set; }
public string Product { get; set; }
public decimal Quantity { get; set; }
}
public class Order
{
public Order()
{
OrderLines = new OrderLineCollection();
}
public OrderLineCollection OrderLines { get; private set; }
public class OrderLineCollection : IEnumerable<OrderLine>
{
private readonly List<OrderLine> internalOrdersList = new List<OrderLine>();
internal OrderLineCollection() {}
public int Count { get { return internalOrdersList.Count; } }
public void AddOrder(string product, decimal quantity)
{
internalOrdersList.Add(
new OrderLine
{
Product = product,
Quantity = quantity
}
);
}
public IEnumerator<OrderLine> GetEnumerator()
{
foreach (var order in internalOrdersList)
{
yield return order;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
Här bryter vi mot både LOD och ROLS, Vi har en lista som inte beter sig som en lista (ingen implementation av IList) och vi har en metod som ligger en bit ner (även om den faktiskt i det här fallet följer domänregler så bryter den mot LOD).
Så frågan är, skall vi alltid slaviskt följa LOD eller ROLS? ROLS är f-ö det MS hävdar är problemet med aspekter ;)
Poängen här är att om vi ser till att inte bryta mot LOD genom att ändra koden till följande:
public class Order
{
public Order()
{
OrderLines = new OrderLineCollection();
}
public OrderLineCollection OrderLines { get; private set; }
internal void AddOrder(string product, decimal quantity)
{
OrderLines.AddOrder(
new OrderLine
{
Product = product,
Quantity = quantity
}
);
}
public class OrderLineCollection : IEnumerable<OrderLine>
{
private readonly List<OrderLine> internalOrdersList = new List<OrderLine>();
internal OrderLineCollection() {}
public int Count { get { return internalOrdersList.Count; } }
internal void AddOrder(OrderLine newLine)
{
internalOrdersList.Add(newLine);
}
public IEnumerator<OrderLine> GetEnumerator()
{
foreach (var order in internalOrdersList)
{
yield return order;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
Så har vi ju faktiskt en ganska snygg lösning, men vi bryter mot ROLS om utvecklaren förväntar sig ett beteende likt det som är i ramverket (dvs List<T>). Så ytterligare en fråga vi måste ställa oss, är det möjligen så att vi skall kräva att alla domänobjekt skall ha ett generiskt beteende som i ramverket, eller kan man möjligen förvänta sig att utvecklarna lär sig reglerna runt de domänobjekt de jobbar med?Sv: Law of demeter och .NET med Collections
AOP bryter bara mot ROLS om man tillåter interception av non virttual, private , sealed members.
Håller du dig till AOP via subclass proxies så bryter det inte mer mot ROLS än OOP ;-)
Ang LOD.
Om hela syftet är att gömma ett objekts interna struktur genom att bara ha metoder som "AddOrderLine(string product,double quantity)" , så får du ju isåfall inte returnera OrderLines objekt på något ställe.
Annars har du bara gömmt strukturen lite.
Jag köper inte att regeln skulle vara definierad så att man inte får skicka in objekt men att returnera dem går bra..
Att många använder metoder som "AddOrderLine" på "Order" när de jobbar med domänmodeller anser jag inte har ett dugg med LOD att göra utan snarare att man vill undivka att skapa extra klasser och speciella beteenden för sina listor.
Det handlar bara om enklaste lösningen för att få interception så att du kan sätta ny state på din order efter att du addat en orderline..
inte att du vill gömma någon struktur...Sv:Law of demeter och .NET med Collections
>Jo, ROLS säger att du ska använda det sätt som gör användaren minst förvånad.
>Eftersom vi har intefaces / kontrakt i .NET som är till för listor så bryter det mot ROLS att inte använda >dessa interfaces.
Alltså, stirra inte blint på Add metoden här. Jag menar att ROLS säger inte att man skall gå via en klass för att gå vi dess returklass metoder. Det är det jag menar här. Dvs. ClassB är en lista jag förväntar mig ju själklart en Add dvs ROLS. Men om man går via ClassA till ClassB för att sen anropa Add bryter man mot LoD men ClassB bryter inte ROLS. Men ClassA är ingen ROLS då man inte vet vad man förväntar sig av den.
ClassA.CLassBs.Add
bryter mot LoD, vilket man helst skall undvika enligt LoD, Detta har inget med ROLS att göra då man inte förväntar sig att ClassA skall ha en ClassB den är en intärn implementationsdetalj.
ClassB.Add
bryter inte mot ROLS och inte heller mot LoD
>> Men det finns inget som säger att du inte får hämta ClassB för att sen anropa dess metod.
>Att mellanlagra objektet i en tmp variabel gör inte ditt program mindre buggfritt på något sätt.
>På vilket sätt menar du att du får en mindre buggig applikation genom att mellanlagra resultatet av ett >metodanrop i tmp variabler utan att tillföra några conditions på dessa?
Problemet är Mockning via TDD. ClassB finns inte när du skapar ClassA, du mockar därför ClassB innan den skapas. Sen skapar man ClassB. Problemet med att då ha kod ClassA.CLassB.ClassBMetod är att du går genom ClassAs implementation för att anropa en returklass metod som inte finns.
Ett enhetstest i ex TDD skall testa alla Enheter om du skriver koden ClassA.CLassB.ClassBMetod i din kod så har du inge test på denna eller hur? och du testar inte denna raden kod i enhetstestet heller för det är ingen enhet. Du testar dock ClassA.DoSomthingThatUseClassBMetod.
Har en rätt bra pdf där ex Thoughtworks skrivit en del om detta, men den får jag skicka via MSN om du vill ha?
Sedan ökar även läsrbarheten i koden genom att göra en metod på ägaren. Som med hunden.
dog.Body.Tail.Wag
Vad gör man? viftar, men varför?
dog.ExpressHappiness
Nu förstår jag... man viftar för att visa att hunden är glad. Så LoD ger även OO/OOP fördelar så som många andra principer ger. Sen är det ju oxå så att alla principer inte behöver följas, men många är trevliga och hjälper till o ger helt ok kod. LoD gillar jag... ROLS gillar jag oxå i vissa fall, då de är mer logiskt att ha dem.
Mvh JohanSv:Law of demeter och .NET med Collections
>Om hela syftet är att gömma ett objekts interna struktur genom att bara ha metoder >som "AddOrderLine(string product,double quantity)" , så får du ju isåfall inte returnera OrderLines >objekt på något ställe.
Nej LoD säger inget om att man skall gömma, Den säger bara att man inte skall gå via en klass intärna klasser för att sen anropa dess metoder utan man skall från grundklassen istället ha en metod som gör det.
Alltså.
Order.AddOrderLine(product,3); <--- LoD
nyttjar i sin tur dess intärna List av exempelvis OrderLine
AddOrderLine(Product product, int quantity)
{
_orderLine.Add(new OrderLine(product,quantity); <--- ROLS
}
Men man måste inte Gömma så man inte kommer åt OrderLine från Order om man inte vill det.
Dvs:
OrderLine orderLines = order.OrderLines; <-- LoD
Men att göra
order.OrderLine.Add(product,3) <--- Bryter LoD
Så LoD handlar mer om en Guidelineregel dvs en princip man som utvecklare kan följa.
Dvs det är du som utvecklare som har regel i ryggmärgen bara en . om fler då behöver jag nog göra en wrappermetod/fasadmetdo på min ägare. Aggregat root eller liknande.
Mvh JohanSv: Law of demeter och .NET med Collections
ClassA.CLassBs.Add
är precis exakt samma sak som
i IL :classA.ClassBs.Item[index]
i c# : assA.ClassBs[index]
Så varför menar du att accessa en viss metod är OK men inte en annan?
>>dog.Body.Tail.Wag
>>Vad gör man? viftar, men varför?
>>dog.ExpressHappiness
Det där är inte det exempel jag frågade om , att skriva kod som tydligt uttrycker dina intentioner är bra.
jag undrade om vad för nytta du ansåg att mellanlagra något i tmp variabler ger..
>>Problemet är Mockning via TDD.
LOD skapades bra långt innan det fanns mocking ramverk eller TDD, så det är inte orsaken till det...
och jag ser inte va du menar att mellanlagring i tmp variabler skulle ha för inverkan när man mockar?
visa exempel?Sv:Law of demeter och .NET med Collections
Det finns många regler gällande accessa aggregat via ägaren i OO, ex i DDD så säger Evans att man inte skall kunna accessa ett aggregat utan att hämta det från sin Aggregat root.
Vi kan ta:
dog.Body.Legs.Add(leg);
vs
dog.AddLeg(leg);
LoD ger ökad förändringsbar och läsbar kod vs icke Lod. Det är ett argument. Men på senare tid har även LoD gett fördelar ang TDD med mocking och jag var inne på varför så tar det inte igen.
Om du byter implementation på Body i första exemplet, säg att du gör Leg private då kommer nog en hel del kod att fallera i din applikation. Ju mer . du traverserar genom ju större risk till redundant kod. I ett annat skede kanske du gör.
dog.Body.Legs.Move etc etc...Även här får du kompileringsproblem och måste ändra en massa kod för att du går genom en klass returnerande insants instans metod.
Så genom att ha:
dog.RemoveLeg ...
samt AddLeg på Agregatrooten så kommer förändring av Body eller Leg inte påverka mer än implementationen av dess agrregatroots metoder. Dvs ungefär samma skäl till varför man skall undvika redundant kod...
Sen minskar du faktiskt läsbarheten av koden genom massa A.B.C.D.F... då du inte direkt vet vad du gör.
LoD kommer automatiskt att kräva att din metod förklarar vad dina krav. Så det blir ännu lättare att förstå din kod för du har mer beskrivande namn för din uppgift.
Måste referera till Wag exemplet igen för tycker det är rätt klockrent.
Kravet/user Story: Visa att hunden är glad.
dog.Body.Tail.Wag(); säger inte tyvärr inget ang kravet.
men
dog.ExpressHappiness(); säger precis vad ens krav/User story vill ha sagt.
Plus att du då enkelt kan förändra din Body, din Tail din Wag utan att behöva ändra saker på flera ställen för att du faktiskt har implementationen på dess Agregatroot.
Grejen med bra kod, dvs lättförklarig kod handlar inte om hur IL omvandlar din kod utan hur utvecklaren läser koden i ex dess IDE så som i VS .Net. Hur det blir i bakgrunden behöver vi inte ens bry oss om för det är inte den koden nästa utvecklare eller du själv läser för att sen underhålla.
Återigen måste man ju inte gå efter principer eller i alla fall inte många av dem.
Jag gillar LoD men det betyder inte att alla måste göra det :-)
mvh Johan