webForumDet fria alternativet

Singleton eller inte?

58 svar · 2 969 visningar · startad av Lukaspojken

LukaspojkenMedlem sedan maj 20011 312 inlägg
#1

Jag börjar fundera på om man inte ska börja köra singleton på sina servicar och repositories men jag är osäker på om det kan skapa problem.

Jag vill alltså undvika behöva skriva två rader kod, dvs:

OrderRepository repository = new OrderRepository();
Order order = repository.GetById(orderId);

Så här känns bättre:

Order order = OrderRepository.GetInstance().GetById(orderId);

Men kan det bli problem om man kör detta? Det är i webbapplikationer och inte windowsapplikationer som jag tänkt använda mig av detta?

Static/shared vill jag inte använda mig av.

GladhMedlem sedan maj 20012 812 inlägg
#2
new OrderRepository().GetById(orderId);

Så slipper du singelton.

Eller så skriver du följande.

public class OrderRepository{
  public static GetNewInstance(){
    return new OrderRepository();
  }
}

Order order = OrderRepository.GetNewInstance().GetById(orderId);

Kör du dem som singelton så får du hålla reda på trådsäkerheten själv, även om jag inte tror att du har några delade resurser i ditt repository, men helt klart blir det så att flera trådar kommer dela på samma objekt och det kan ställa till det för dig i slutändan.

Static/shared vill jag inte använda mig av.

Hur i hela världen har du tänkt få ett Singelton objekt utan att använda dig av static på själva instans-objektet!?

Kom på en sak som inte talar för din singelton lösning, och det är när du använder dig av DependencyInjection så är Singelton-ingen bra lösning..., och DI vill man gärna använda för Unittester...

- M

LukaspojkenMedlem sedan maj 20011 312 inlägg
#3

Tack för ditt svar!

Detta gillar jag inte:
new OrderRepository().GetById(orderId);

Det känns på något sätt inte rätt :)

Denna lösning är intressant:
public static GetNewInstance(){
return new OrderRepository();
}

När jag skrev att jag inte vill använda static/shared så menade jag att jag inte ville använda det på alla metoder i tex repositoryn. Var lite otydlig...

Det med DependencyInjection är ett argument att inte använda singleton men detta kan man lösa genom en integrationswrapper istället. Jag har testat att skapa en sådan och det blir mycket renare och utvecklarvänligare kod. Så det argumentet har jag löst.

Det med trådsäkerheten är dock intressant. Finns det några nackdelar med GetNewInstance-sättet? Eller är det bättre än singleton? För om jag förstår dig rätt så menar du att trådsäkerheten är löst via användningen av GetNewInstance.

NickemannenMedlem sedan aug. 20003 575 inlägg
#4

Jag tycker inte man skall använda sig av singleton, hur gör du en sådan repository testbar?

Static fungerar ju inte med interface t.ex. hur skall du göra dina integrationstester?

Jag tycker inte heller att new XXXRepository().XXXX är bra heller. Jag röstar för dependency injection här.

Klassen där du arbetar mot din repository bör få repositoriet via konstruktorn. Då slipper man att tänka på trådning i en webbapp samt att testningen löses :).

SPiNMedlem sedan mars 20007 896 inlägg
#5

Finns det några nackdelar med GetNewInstance-sättet?

Det kommer ju att skapas ett nytt OrderRepository-objekt vid varje metodanrop, om du inte sparar undan objektet. Jag tycker inte om den lösningen just på grund av detta.

Order order = OrderRepository.GetNewInstance().GetById(orderId);
List<Order> list = OrderRepository.GetNewInstance().GetByProduct(productId);
List<Order> anotherList = OrderRepository.GetNewInstance().GetByTime(time);
...

Versus:

OrderRepository or = new OrderRespository();
/* Alternativt: */
OrderRepository or = OrderRepository.GetNewInstance(); // <- Helt onödigt, lika bra att använda 'new'

Order order = or.GetById(orderId);
List<Order> list = or.GetByProduct(productId);
...

Det kan ju bli ett bra sätt att förbruka oanvänt minne å andra sidan. ;) Bygg in någon sorts cache-funktionalitet till ett Singleton om du prompt vill ha koll på trådsäkerheten också, förutom att du slipper använda 'new'.

Edit, Nickemannen's förslag gillar jag dock.

LukaspojkenMedlem sedan maj 20011 312 inlägg
#6

--> Nickemannen
>Jag tycker inte man skall använda sig av singleton, hur gör du en sådan repository testbar?

Jag har inte jobbat så mycket med enhetstester av repositories men kan man inte lösa det med en integrationswrapper som mockar åt dig du väl kör testerna? Vill du köra integrationstester så fixar integrationswrapper det åt dig också. Antingen att du kör integrationstester mot utvecklingsdatabasen eller mot en testdatabas som skapas om vid varje testkörning.

Fördelen med att använda sig av en integrationswrapper är att du slipper interface-hell :) Du får också mycket renare och utvecklarvänligare kod.

>Static fungerar ju inte med interface t.ex. hur skall du göra dina integrationstester?

Men singleton fungerar väl för integrationstester? Eller hur menar du?

>Jag tycker inte heller att new XXXRepository().XXXX är bra heller. Jag röstar för dependency injection här.

Den klassiska implementeringen av Dependency Injection är överreklamerat :) Det stökar till din arkitektur mer än du behöver. Det låter fint med flexibilitet och lösbarhet men det ligger en kostnad bakom detta som man oftast glömmer. Det finns alternativ till DI och som ger samma resultat och för en billigare peng.

>Klassen där du arbetar mot din repository bör få repositoriet via konstruktorn. Då slipper man att tänka på trådning i en webbapp samt att testningen löses

Man skulle kunna göra så att man överlagrar singleton metoden GetInstance och låter en metod ta inparametern "bool createNewInstance" och denna anger om det ska köra den befintliga instancen eller skapa en ny. Detta borde hjälpa en att lösa eventuella trådskonflikter.

--> SPiN
>Det kommer ju att skapas ett nytt OrderRepository-objekt vid varje metodanrop, om du inte sparar undan objektet. Jag tycker inte om den lösningen just på grund av detta.

Sant! Det bästa är nog att överlagra singleton metoden Getinstance så som jag nämnde för Nickemannen. Eller vad tror du den lösningen?

spangoMedlem sedan juni 20008 205 inlägg
#7

Lukaspojken skrev:

>Klassen där du arbetar mot din repository bör få repositoriet via konstruktorn. Då slipper man att tänka på trådning i en webbapp samt att testningen löses

Man skulle kunna göra så att man överlagrar singleton metoden GetInstance och låter en metod ta inparametern "bool createNewInstance" och denna anger om det ska köra den befintliga instancen eller skapa en ny. Detta borde hjälpa en att lösa eventuella trådskonflikter.

--> SPiN
>Det kommer ju att skapas ett nytt OrderRepository-objekt vid varje metodanrop, om du inte sparar undan objektet. Jag tycker inte om den lösningen just på grund av detta.

Sant! Det bästa är nog att överlagra singleton metoden Getinstance så som jag nämnde för Nickemannen. Eller vad tror du den lösningen?

Du anstränger dig väldigt mycket för att lösa ett problem du inte behöver ha. Vad är det som är så jobbigt med att ta emot repository-objektet via konstruktorn? Vill du inte ha en extra variabel eller vad är det frågan om?

LukaspojkenMedlem sedan maj 20011 312 inlägg
#8

-> Spango
>Du anstränger dig väldigt mycket för att lösa ett problem du inte behöver ha. Vad är det som är så jobbigt med att ta emot repository-objektet via konstruktorn? Vill du inte ha en extra variabel eller vad är det frågan om?

Anledningen för att använda singleton är att få ner antalet kodrader samt att erhålla en utvecklarvänligare arkitektur. Skickar man ned repositoryn så tappar man bort tex F12 stegningen vilket inte är utvecklarvänligt.

Måste gå nu :)

erkaMedlem sedan dec. 19996 522 inlägg
#9

Den klassiska implementeringen av Dependency Injection är överreklamerat Det stökar till din arkitektur mer än du behöver. Det låter fint med flexibilitet och lösbarhet men det ligger en kostnad bakom detta som man oftast glömmer. Det finns alternativ till DI och som ger samma resultat och för en billigare peng.

Jag tror inte du har förstått DI riktigt, eller arbetat med helt fel DI-ramverk. Med tex structure-map så märker du knappt av att du har ett DI-ramverk.

Att motivera ett arkitekturval med att det går snabbare för utvecklarna att hoppa via F12, då är man ute på hal is i mitt tycke.

Sedan tycker jag det är helt skilda test integrationstest och repository enhetstest.Som utvecklare vill du ha snabba feedbackloopar oavsettom du kör TDD eller inte, annars kan du likt väl strunta i enhetstesta över huvudtaget. Det får man inte med en databasbaserad enhetstestning, tom. inmemory-varianter tenderar att bli för långsamma.

integrationswrapper är att du slipper interface-hell

Det är skillnad på interface-hell och bra strukturerad och löst kopplade moduler.

NickemannenMedlem sedan aug. 20003 575 inlägg
#10

Jag är nog benägen att hålla med dom andra i tråden att F12 approachen inte är 100-procentig. Även om jag gillar att du strävar efter hur vi kan få mer utvecklarvänlig kod.

Tidigare gillade jag också Singleton i många fall, men efter att ha läst på mig om DI, testning och hur man bygger mer löskopplade system så tycker jag att injections via kontruktorn is the way to go.

Jag känner att jag vill inte låta mina objekt ha rättigheten att välja infrastruktur det är något jag vill kunna konfigurera utanför (går att lösa riktigt smidigt med olika ramverk som t.ex. StructureMap eller ms variant Unity som är riktigt trevliga verktyg).

Kikar man på Ms CompositeWPF ramverk så bygger det mer eller mindre på en DI-container och jag måste bara säga I LIKE!

Utvecklar du din applikation som är injection drivet (nytt begrepp?) så behöver du nog sällan behöva tänka på trådar, samtidiga användare osv :).

Jag kan inte tala för alla men jag känner att just nu är det detta sättet jag anser vara det bästa och trevligaste sättet att arbeta på, speciellt när jag enkelt kan växla mellan Mocks och riktiga implementationer.

Och som jag och alla andra skrev testbarheten ökar markant :D

erkaMedlem sedan dec. 19996 522 inlägg
#11

Icke testbar kod är i min värld synonymt med skräpkod. Utvecklarvänlig kod handlar om att skapa en bra kodbas som är lätt att förstå, en bra utgångspunkt är enligt mig att följa SOLID-principles (http://www.lostechies.com/blogs/chad_myers/archive/2008/03/07/pablo-s-topic-of-the-month-march-solid-principles.aspx) där bland annat "Dependency Inversion Principle" ingår, depend on abstraction not on concretions. (http://www.objectmentor.com/resources/articles/dip.pdf).

Jag tycker som många andra att singelton är ett anti-pattern, på grund av att det går emot SOLID-principen Single Responsibility Principle, genom att den blandar ett objekt med logik för hur den skapas och lever, 2 ansvar i samma klass, samt att det är jävligt svårt att testa singeltons, det blandar uröver detta in massa annat otrevligt såsom global state i din applikation.

NickemannenMedlem sedan aug. 20003 575 inlägg
#12

erka skrev:

Icke testbar kod är i min värld synonymt med skräpkod. Utvecklarvänlig kod handlar om att skapa en bra kodbas som är lätt att förstå, en bra utgångspunkt är enligt mig att följa SOLID-principles (http://www.lostechies.com/blogs/chad_myers/archive/2008/03/07/pablo-s-topic-of-the-month-march-solid-principles.aspx) där bland annat "Dependency Inversion Principle" ingår, depend on abstraction not on concretions. (http://www.objectmentor.com/resources/articles/dip.pdf).

Jag tycker som många andra att singelton är ett anti-pattern, på grund av att det går emot SOLID-principen Single Responsibility Principle, genom att den blandar ett objekt med logik för hur den skapas och lever, 2 ansvar i samma klass, samt att det är jävligt svårt att testa singeltons, det blandar uröver detta in massa annat otrevligt såsom global state i din applikation.

Håller fullständigt med :D, dock finns det ju alltid undantag :)

erkaMedlem sedan dec. 19996 522 inlägg
#13

Hur man kan bedriva agile-utveckling utan att ha stöd av av xp-tekniker såsom continious integration, automatiserade testsviter med snabba feedback loopar är för mig helt ofattbart. Det är ytterst få saker som jag någonsin stött på som inte är testbart med ett atomärt enhetstest, inte integrationstest. Att få saker testbart är ju bara en av födelarna med DI.

Man TDD:ar inte för att få fram testtäckning, man TDD:ar för att driva designen framåt med hjälp av test. Red, Green, Refactor, tyvärr missar väldigt många den sista biten av mantrat. Undantag finns alltid, har man förstått DI och gjort en sund implementering så ska man inte märka av det som utvecklare i speciellt stor utsträckning. Med Structure Map kan du ju även få objekt att leva som singeltons :) SOLID-principles är bara vanlig klassisk objektorientering, inget nytt.

Google Testing Blog skriver en artikel alla som läser denna tråd bör kika i, förutom att kör de .net bör man kolla in Structure Map
http://misko.hevery.com/2008/11/11/clean-code-talks-dependency-injection/

http://googletesting.blogspot.com/2009/01/when-to-use-dependency-injection.html

Men bryr man sig inte om unit tests finns det ingen anledning att köra DI, kanske, om man inte bryr sig om löst kopplade system, då kan man fortsätta ha sina sköna duster med new operatorn och starkt beroende mellan klasser, vilket man underlättar att inte frambringa med DI. Jag vill kunna ändra något i en viss klass utan att behöva bry mig om att jag påverkar något annat, och om jag gör det så märker jag det inom 3-sekunder för min testsvit är inte längre grön.

Red, det du försöker göra (getinstance) är en variant av service locator.
http://martinfowler.com/articles/injection.html#ServiceLocatorVsDependencyInjection, vilket skiljer sig från DI genom att DI är push (Hollywood Principle, don’t call us, we’ll call you) och service locator är pull (Be om en konkret klass runtime)

LukaspojkenMedlem sedan maj 20011 312 inlägg
#14

För ett tag sedan skrev jag jag ett blogginlägg om detta med DI, testbarhet och löskopplade delar. Jag vet att inte många håller med mig men det jag skriver är saker jag snappat upp under en mängd samtal med olika personer (erfarna som inte så erfarna i DDD, XP m.m.).

Jag gör några utdrag från inlägget:

"Ett populärt syfte med att använda DI är att förbättra och snabba upp testbarheten av systemet genom tex mockning. Om DI till stor del baseras på detta syfte finns det en risk för suboptimering, dvs man optimerar testbarheten av systemet på bekostnad av renhet och enkelhet i den tekniska arkitekturen."

"Jag gillar heller inte att arkitekturen smutsas ned med kod bara för att förbättra testbarhet av systemet. Testbarhet har egentligen inget med systemet i sig att göra och därför bör den påverka arkitekturen så lite som möjligt."

"För att avrunda detta blogginlägg så kan man säga att en förbättrad testbarhet av systemet är förenligt med en ren och enkel teknisk arkitektur. Lösningen för att uppnå denna förening är inte genom en omfattande implementering av Dependency Injection utan genom att skapa wrappers runt olika integrationslösningar."

"Kanske bör jag tillägga att jag också är skeptisk till en överanvändning av DI för att uppnå flexibilitet i arkitekturen. Allt behöver inte vara flexibelt bara för att det går. I vissa fall behövs det medan i andra fall är det överdesign. Denna överdesign är en slöseri med kundens pengar och något som inte främjar enkelhet och renhet i den tekniska arkitekturen. Flexibilitet låter bra men den har sina konsekvenser som måste beaktas."

Hela inlägget finns här:
http://kauppi.levasunt.nu/post/Dependency-injection.aspx

Det är intressant att agile tas upp men agile handlar inte bara om att utveckla fungerande mjukvara utan det handlar också om att se till att kunden erhåller en så bra avkastning på sitt investerade kapital som möjligt. En överproduktion av automatiserade tester är jag helt säker på är oftast slöseri med kundens pengar liksom en underproduktion. Det gäller att finna en bra nivå på testning.

erkaMedlem sedan dec. 19996 522 inlägg
#15

Det är intressant att agile tas upp men agile handlar inte bara om att utveckla fungerande mjukvara utan det handlar också om att se till att kunden erhåller en så bra avkastning på sitt investerade kapital som möjligt. En överproduktion av automatiserade tester är jag helt säker på är oftast slöseri med kundens pengar liksom en underproduktion. Det gäller att finna en bra nivå på testning.

Automatiserade tester är en agil grundprincip. Prio ett är fungerande mjukvara det är inget i det jag säger som går emot det, handlar det om prototyping etc, kör scuffolding. Men ska det bli en applikation som kommer fortsätta leva (vilket majoriteten gör) är välskrivna testsviter en så otroligt viktig punkt. Du skriver inte tester för testernas skull, du skriver tester för att driva designen framåt. Du har atomära enhetstester som är snabba för att få snabb feedback, skapa tillit hos utvecklarna, att de de förändrar inte påverkar något annat. Det fungerar också som en dokumentation över krav, se BDD och jag tror det kommer påverka sättet vi utvecklar de närmsta åren. Du TDD:ar också för att få fram en så ren design som möjligt, det jag skrev att många missar och fokusera på refactor steget i red green refactor. Att testdriva hjälper dig inse kraven tydligare och även på det viset bidra till renare kod.

Jag tror du inte du inser skillnaderna i olika typer av testning. Integrationstester är en helt annan sak. Visst det går fortfare att cowboy-koda och bara ha testteckning lite var stanns i din kodbas, vilket aboslut inte kommer resultera i att utvecklare misstrot testsviter och förvandlar dem till en stor jävla code-smell.

Ska jag vara ärlig fattar jag inte din blogpost vad menar du med tex "standardiserat stöd för mockning av objekt". Kör ett mocking ramverk som RhinoMocks.

"mock-objekten och resultaten i en databas" Ytterligare ett dependency och mer kod att underhålla. Varför inte köra test-fixtures som hjälper till att öka förståelsen för domänen.

Över det stora fattar jag inte vad det är du vill testa, hur testar du tex. XMLDocumentReader-klassen med ett unit-test om klassen ser ut exempelvis så här, med en integrationswrapper, utan att bryta mot definitionen av ett enhetstest. Den här klassen är omöjlig att testa och bryter mot en del olika principer som har med god oop-sed att göra, varför ist. injecta XMLParser i konstruktor.

public class XMLDocumentReader
{
    private XMLParser Parser { get; set; }

    public  XMLDocumentReader()
    {
        Parser = new XMLParser();
    }

    public XMLDocument ParseFile(string fileName)
    {
        //Do some work throu the Parser
        return null;
    }
}

Risk för suboptimering, dvs man optimerar testbarheten av systemet på bekostnad av renhet och enkelhet i den tekniska arkitekturen."

Ett system blir enklare, och renare med DI, om sedan utvecklarna har svårt att hänga med i hur en bra uppbyggt klass fungerar är att annat problem, skaffa bättre utvecklare eller kanske gör det som är att föredra, utbilda dem i varför. Enda suboptimeringen är ju om man prototypar eller spikar något, kör man med DI och designprinciper då är det suboptimering, så länge man slänger skräpet när man är klar, och påbörjar den riktiga utvecklingen.

Ett populärt syfte med att använda DI är att förbättra och snabba upp testbarheten av systemet genom tex mockning. Om DI till stor del baseras på detta syfte finns det en risk för suboptimering, dvs man optimerar testbarheten av systemet på bekostnad av renhet och enkelhet i den tekniska arkitekturen."

Det är en av anledningarna, enklare underhåll, mindre hårt kopplade klasser, öppnare och renare arkitektur är de stora andra.

Så du menar att fokusera på hög cohesion och låg coupling är suboptimering, för exakt det är effekterna av DI? För mig är det bara bra yrkessed och erfarenheten att om du misslyckas med det kommer det garanterat bita någon i röven inom kort. Återigen det är olika typer av test, DI har ingenting direkt att göra med integratonstester, även om det underlättar integrationstester också. Vilket är en viktig poäng, integrationstester testar genom att koppla ihop koden som redan har enhetstestats. Integrationstester och enhetstester har helt olika syften.

GladhMedlem sedan maj 20012 812 inlägg
#16

Som jag ser det så verkar du (lukaspojken) hänga upp dig på DI är till för testbarhet. Och det är ju en sak du kan använda det till. Men själva idén med att ge ett objekt de objekt som det behöver för att klara av sitt arbete, istället för att detta objekt själv skall skapa de objekt de behöver är ju möjligheten att få löstkopplade system.

Att jobba mot interface är ju inte bara till för att kunna testa sina system på ett enkelt sätt, utan bygger ju på att man skapar löstkopplade system. Fördelen är att du när som helst kan byta ut vilka delar du vill utan behöva ändra på din kod i de objekt som använder sig av dessa interface. Det finns ju folk som går lite på överdrift till andra hållet och bara använder interface i sin kod och inga instanser av objekt, det är till och med rekomendationer att man skall göra så, men personligen så anser jag det lite som överkill att alla mina entiteter skall ha ett interface som jag jobbar mot, eftersom dessa entiteter finns inne i min modell.

Men allt annat som finns utanför min modell, så som Repositorys, bör ju ha ett interface som man jobbar emot, för att man skall slippa den hårdakoppling som en referens till ett objekt ger.

Men alla har vi olika synsätt på hur det skall vara, och om 10 år så kanske man tittar tillbaka på hur vi utvecklade och undrar hur funttade var vi som använde DI och Unittester när man kunde göra X och Y istället...

Jag har dock lite svårt att se hur du skall göra bra unittester av din kod utan DI? Du får gärna förklara mer hur du har tänkt att lösa kod istället om du inte använder DI.

bara ett exempel som jag skriver rakt nu ur huvudet.

public class GetUserAndOrderServices {
  private IUserRepository _userRepository;
  private IOrderRepository _orderRepository;

  public GetUserAndOrderServices(IUserRepository userRepository, IOrderRepository orderRepository)
  {
      _userRepository = userRepository;
      _orderRepository = orderRepository;
   }

   public User GetUserAndOrders(int userIdentifier)
   {
        User user = GetUser(userIdentifier);
        user.Orders = GetOrdersByUserId(user.Identifier);

        return user;
   }
   private User GetUser(int userIdentifier){
     return _userRepository.GetUsers(userIdentifier);
   }
   private IList<Order> GetOrdersByUserId(int userIdentifier)
  {
      return _orderRepository.GetOrdersByUserId(userIdentifier);
   }
}

I följande exempel så gör vi unitester för att kontrollera så att vi får rätt antal order tillbaka vid rätt användare. NU är det tyvärr så att Usern ligger placerad i en stordator miljö och för att komma åt denna så måste man anropa en BizTalk-server som i sin tur anropar en Javamodul som i sin tur hämtar upp informationen från stordator miljön. I genomsnitt så tar det ungefär 3 sekunder att hämta en användare.

Alla orders ligger dock lite bättre till via en WCF-services, men vid varje första anrop så gör en hel del säkerhetskontroller så det tar också 3 sekunder att få dessa orders vid första anropet, om programmet sedan fortfarande är uppe så går det fortare. Vi en enhetstest skulle det alltså ta 6 sekunder att hämta denna information om vi går mot de riktiga databasern eller någon av deras testmiljöer. Så för att göra det fortare så Mockar vi datan istället och programmerar in det själv vilken data som skall returneras vid testen.

Hur löser du det med din Integrationswrapper och utan DI? Det är jag nyfiken på att få se...

- M

NickemannenMedlem sedan aug. 20003 575 inlägg
#17

Lukaspojken skrev:

"Ett populärt syfte med att använda DI är att förbättra och snabba upp testbarheten av systemet genom tex mockning. Om DI till stor del baseras på detta syfte finns det en risk för suboptimering, dvs man optimerar testbarheten av systemet på bekostnad av renhet och enkelhet i den tekniska arkitekturen."

"Jag gillar heller inte att arkitekturen smutsas ned med kod bara för att förbättra testbarhet av systemet. Testbarhet har egentligen inget med systemet i sig att göra och därför bör den påverka arkitekturen så lite som möjligt."

"För att avrunda detta blogginlägg så kan man säga att en förbättrad testbarhet av systemet är förenligt med en ren och enkel teknisk arkitektur. Lösningen för att uppnå denna förening är inte genom en omfattande implementering av Dependency Injection utan genom att skapa wrappers runt olika integrationslösningar."

"Kanske bör jag tillägga att jag också är skeptisk till en överanvändning av DI för att uppnå flexibilitet i arkitekturen. Allt behöver inte vara flexibelt bara för att det går. I vissa fall behövs det medan i andra fall är det överdesign. Denna överdesign är en slöseri med kundens pengar och något som inte främjar enkelhet och renhet i den tekniska arkitekturen. Flexibilitet låter bra men den har sina konsekvenser som måste beaktas."

Jag håller tyvärr inte med dig på någon av dina punkter här, på vilket sätt smutsar DI ner din arkitektur?
Jag tycker mer tvärtom, din arkitektur blir renare, det finns inte flera dependencies som gör det svårt för dig att underhålla koden.

Varje klass gör sin sak, oberoende av andra, klassen bestämmer inte vilken teknik den skall använda utan det bestäms av skaparen av klassen, jag tycker det blir mycket renare på så viss, att det blir mer kod tror jag inte heller.

När det gäller överdesign så håller jag inte heller med, dock är det en tröskel att förstå hur DI containers fungerar för att komma igång med det, men DI i sig är inget nytt :).

johannormenMedlem sedan sep. 200888 inlägg
#18

Överdesign får man då man överarbetar något som inte behöver vara med bara för att man envist skall följa alla världens patterns och principer utan att veta varför och när man skall använda dem... För många använder dem bara för att utan att egentligen förstå varför att :)

Vad gäller DI så är det bra i många sammanhang, främst använder jag dem mkt i factories som jag enkelt vill kunna expandera etc, använder det för plugin-pattern etc... Allt för att inte bryta mot OcP (Open closed Principle)

ang

var x = new RepositoryFactory.Create.....

Så tycker jag inte det är speciellt fult, förutom att man egentligen helst skall undvika ha new med i sin kod. Men på vissa ställen måste de tyvärr finnas för någonstans måste ändå sakerna skapas.

Att använda static på factory klasser ser jag inte som något problem så länge ev innehåller i metoden inte bryter mot de vanligaste principerna.
En factory är alltid ett undantag från vanliga objekt då den skall vara smartare och kan krävas vara med pluggbar oxå. Där av är DI väldigt trevlig teknik i just factories.

DI är även smidigt i tester där man vill "koppla" bort objekt som man inte vill skall ingå i själva testerna. Ex DB access, Viss Loggning m.m. m.m.

Mvh Johan

LukaspojkenMedlem sedan maj 20011 312 inlägg
#19

--> erka
Jag tror du missförstår mig. Jag förstår skillnaden mellan integrations- och enhetstester. Det jag vill åt är att i de fall syftet är att undvika integrationstester så förespråkar jag användningen av en integrationswrapper istället för användning av DI. Detta för att erhålla en renare och utvecklarvänligare arkitektur. Jag vill alltså att testimplementeringen är så nära integrationen som möjligt. Vi antar att du har en OrderRepository som implementerar ett interface med syftet att man ska kunna tex mocka objekt. Jag skulle hellre använda mig av en integrationswrapper och skipa interface:t. På detta sätt kan jag mocka objekt på inparameterar samt testa min OrderRepository djupare än om jag skulle använt mig av ett interface. Jag kan till och med validera tex sql-sats som är mappad till det mockade objektet, dvs att tex kolumnnamn är mappade korrekt utan att exekuera frågan.

Nja, automatiserade tester är inte en grundprincip i agil. Eller fungerande mjukvara är inte detsamma som automatiserade tester. Men automatiserade tester är en väg för att uppnå fungerande mjukvara. Det är dock viktigt att beakta att agil bygger på många andra värderingar och principer än enbart fungerande mjukvara. En viktig bit är fokus på affärsnytta.

Jag tycker att affärsnyttan och kunders behov ska styra hur pass väl fungerande mjukvara vi ska utveckla. Du kan säkerligen erhålla fullständig perfektion i fungerande mjukvara med hjälp av bla automatiserade tester men till vilket pris? Har kunden behov av perfektion? Är kunden beredd att betala priset för vad perfektion är? De erfarenheter jag har är att kunden inte har dessa pengar och därför gäller det att försöka hitta en good enough nivå.

Så det handlar inte om Cowboy-programmering utan om att hitta en vettig nivå på testning och löskoppling, samt att undvika att överproducera eller underproducera.

Nedan följer ett grovt exempel på hur en integrationswrapper skulle kunna göras. Om vi tex skapar en basklass till XMLDocumentReader. Det finns andra sätt att lösa detta men nu tog jag bara en av dessa:

public class XMLDocumentReader : XMLDocumentReaderBase
{
    private XMLParser Parser { get; set; }

    public  XMLDocumentReader()
    {
        Parser = new XMLParser();
    }

    public XMLDocument ParseFile(string fileName)
    {
        return //Anrop till basklassen som hanterar integrationen och om XMLdocumentet ska hämtas från filarkivet eller om det ska returnera ett dynamiskt hårdkodat objekt.
    }
}

Samma princip implementerat på Gladhs olika exempel (det kvittar om det är anrop till stordator eller WCF)

public class OrderRepository : RepositoryBase //I detta fall skapar vi en RepositoryBase
{
    public Order GetById(int id)
    {
        //Anrop till basklassen som hanterar integrationen och om ordern ska hämtas från databasen (stordatorn/wcf) eller om det ska returnera ett dynamiskt hårdkodat objekt.
    }
}

>erka: Ett system blir enklare, och renare med DI, om sedan utvecklarna har svårt att hänga med i hur en bra uppbyggt klass fungerar är att annat problem, skaffa bättre utvecklare eller kanske gör det som är att föredra, utbilda dem i varför.

Det handlar inte om att det är svårt att hänga med utan frågan är om det är nödvändigt det man implementerar samt vilket värde det ger. Jag tror det finns alltför mycket överdesign i vår branch. Sen tycker jag i och för sig att kompetens i IT-branchen är relativt låg men det är en annan fråga.

>Gladh: Som jag ser det så verkar du (lukaspojken) hänga upp dig på DI är till för testbarhet. Och det är ju en sak du kan använda det till. Men själva idén med att ge ett objekt de objekt som det behöver för att klara av sitt arbete, istället för att detta objekt själv skall skapa de objekt de behöver är ju möjligheten att få löstkopplade system.

Men varför vill du uppnå ett löstkopplat system? Bara för att man kan uppnå hög flexibilitet så innebär det inte att man ska köra på det. Vi antar att syftet är att löskoppla vår koppling till databasen men hur ofta byter man databas? Att implementera en sådan lös koppling med syftet att om vi någon dag skulle behöva byta databas så kan vi göra det lätt är överdesign och slöseri med kundens pengar.

>Nickemannen: ...att det blir mer kod tror jag inte heller.

Om du tex skapar ett interface till en entitet eller till ett repository hur gör du då för att det inte skulle bli mer kod än om du skulle skipa köra med interface? Vi säger att du har ett repository inteface som tex kräver att GetById, PersistAll m.m. är med. Genom att du bara skapar interfacet så har du mer kod, eller?

>johannormen: DI är även smidigt i tester där man vill "koppla" bort objekt som man inte vill skall ingå i själva testerna. Ex DB access, Viss Loggning m.m. m.m.

I detta fallet tycker jag en integrationswrapper är smidigare att använda än DI. En integrationswrapper har också en mycket större återanvändningsbarhet än ett interface. Den öppnar upp en hel del andra möjligheter också.

GladhMedlem sedan maj 20012 812 inlägg
#20

lukaspojken skrev:

Samma princip implementerat på Gladhs olika exempel (det kvittar om det är anrop till stordator eller WCF)

Ja det kvittar vad du kallar, jag visade bara på ett exempel där man vill få bort att man hämtar data i dess "riktiga" miljö eftersom det skulle bli extremt långsama tester.

lukaspojken skrev:

public Order GetById(int id)
{
//Anrop till basklassen som hanterar integrationen och om ordern ska hämtas från databasen (stordatorn/wcf) eller om det ska returnera ett dynamiskt hårdkodat objekt.
}

Det där var inte mycket till exempel, hur vet din basklass att det är ett test som jag gör eller om det är ett anrop i produktion? Och nej preprocessor direktivet #Ifdebug är inte att rekomendera...

Dessutom så kan du ju ha olika unittester som skall returnera olika resultat, hur implementerar du det i din basklass?

lukaspojken skrev:

Men varför vill du uppnå ett löstkopplat system? Bara för att man kan uppnå hög flexibilitet så innebär det inte att man ska köra på det. Vi antar att syftet är att löskoppla vår koppling till databasen men hur ofta byter man databas? Att implementera en sådan lös koppling med syftet att om vi någon dag skulle behöva byta databas så kan vi göra det lätt är överdesign och slöseri med kundens pengar.

Det är inte speciellt ofta man byter databas, men det är betydligt oftare som man kanske byter sin OR/Mapper, man byter ut sin Loggingklass, man kan ha referenser till externa system via webservices eller WCF som byts ut ganska ofta. Att få ett löstkopplat system med en sådan liten kostnad som DI innebär, är värt det, speciellt ju större system och ju längre tid de skall leva. Det är min erfarenhet!

lukaspojken skrev:

I detta fallet tycker jag en integrationswrapper är smidigare att använda än DI. En integrationswrapper har också en mycket större återanvändningsbarhet än ett interface. Den öppnar upp en hel del andra möjligheter också.

Jag är fortfarande väldigt nyfiken på hur din integrationswrapper ser ut, du kan väl lägga ut lite kod! För jag ser inte fördelarna med dem, och kod hjälper alltid!!!

lukaspojken skrev:

Sen tycker jag i och för sig att kompetens i IT-branchen är relativt låg men det är en annan fråga.

Det håller jag med dig i, det finns ju fortfarande folk som inte tagit till sig sådan enkla hjälpmedel som DI... ;) "Ledsen kunde inte låta bli"

- M

165 ms totalt · 3 externa anrop · v20260731065814-full.30151723
0 ms — hämta forumlista (cache)
0 ms — hämta statistik (cache)
162 ms — hämta tråd, inlägg och bilagor (db)