[.NET] Backwards compatability Interface met Adapter pattern

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • FTL
  • Registratie: Maart 2000
  • Laatst online: 19-09 10:50
Momenteel werk ik aan een project dat een basis platform behelst met daarbovenop als content schermen. Ik wil dat deze schermen als compleet onafhangelijk project moeten kunnen worden geïmplementeerd en alleen middels interface implementatie met het systeem 'communiceren'.

Dit alles zit er al in, schermen worden dynamisch van schijf geladen middels assembly load, geinstantieerd en een menu opgebouwd van schermen die aanwezig zijn.

Nu wil ik dat de schermen functionaliteiten van het basis systeem gaan gebruiken maar dus niet een referentie naar een echte implementatie dll van het basis platform maar middels een interface en op een backwards compatible manier.

Mijn eerste oplossing was:
Er is een Scherm interface die twee methoden gefineerd: GetRequiredInterfaceVersion() en RegisterBasePlatformObject(IPlatformObject)
Deze interface zal nooit wijzigen en hiermee zou het basis platform een object kunnen registreren die de basis platform interface versie (IPlatformObject) implementeerd die het Scherm verwacht.
In IPlatformObject zitten dus functies die sterk onderhevig zullen zijn aan veranderingen.

Dit gaat allemaal goed, zelfs met nieuwe IPlatformObject versies die nieuwe methoden of methoden met nieuwe signiture toevoegen aan de interface.

Maar nu, er zullen methode namen aangepast gaan worden of zelfs verwijderd. Nu zou ik graag na het aanroepen van GetRequiredInterfaceVersion() een object aanbieden die aan de hand van een Adapter design pattern het oude gedrag aanbied aan het scherm, dus de vertaalslag van oude interface naar nieuwe interface.

Op deze manier zullen nieuwe platform versies 100% backwards compatible zijn met oude interface versies.
De interface naam wil ik niet veranderen (dus IBasePlatformV1 enzovoorts) is geen optie. Reflection is ook geen optie, zal wel werken maar valt niet onder categorie 'goede code' IMHO.

Hoe kan ik een object aan het scherm aanbieden die de oude interface implementeerd en toch dezelfde interface naam heeft of beter een alternatief design pattern dat dit probleem op een goede manier oplost?

Acties:
  • 0 Henk 'm!

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Waarom zou je implementatie backwards compatible houden? Zo lang de nieuwe versie alleen maar nieuwe functionaliteit bied is backwards leuk, maar als in een volgende versie het gedrag veranderd wordt het al snel een hel.

Je kunt dus beter zorgen dat extensies aangeven voor welke versie van jouw basis framework geschikt zijn. De meeste Firefox 1.x extensies werken ook niet meer in Firefox 3.5. Stel ik heb een extentie voor versie 1.0 van jouw framework gemaakt. Via unit, regression en acceptatie testen kan ik hier een bepaald kwaliteits label aanhangen.

Maar hoe kan ik de kwaliteit garanderen voor een volgende versie waarvoor ik mijn extensie nog niet heb getest? In de meeste gevallen zal de update geen probleem geven, maar dit zal altijd getest moeten worden en op dat moment kan ook de minimum en maximum versie nummers worden aangepast.

Als de extentie kan opvragen op welke versie van jouw framework draait kan het zelf beslissen welke implementatie het gebruikt. Hiervoor zou het bridge of adaptor pattern kunnen gebruiken.

Backwards comptabiliteit moet iets zijn dat de extensie afdwingt, niet jouw framework. Het enigste wat jouw framework dient te doen is een aantal assemblies scannen op een bepaalde interface. Via reflection kan je vrij eenvoudig een 'versions' attribute uitlezen en controleren of de extensie compatible is met de versie van jouw framework.

Maar heb je al eens gekeken hoe MEF of Windsor Containers extensies hebben geimplementeerd? Heb je codeplex al eens doorzocht. Waarom kies jij voor een eigen extensie framework en gebruik je bijvoorbeeld niet de .net add-in pipeline?

En ik kan jouw nu al garanderen dat 50% van de interfaces bij versie 5 van je framework zijn aangepast of verbeterd.

If it isn't broken, fix it until it is..


Acties:
  • 0 Henk 'm!

  • FTL
  • Registratie: Maart 2000
  • Laatst online: 19-09 10:50
Dank voor je reply. Je biedt meer alternatieven dan een mogelijk oplossing. Had misschien de vraag wat abstracter moeten stellen omdat te voorkomen.

Ik wou eigenlijk niet in discussie over waarom ervoor gekozen is dit is namelijk gewoon een gegeven. In deze situatie moet het framework zorgen dat hij oude plugins gewoon goed ondersteund, wat op zich geen rare eis is mede omdat dit technisch gewoon heel goed haalbaar is. Zie het als een "Must have" in de technische specificatie.

De Adapter compatability laag van het framework zou erg goed (geautomatiseerd) getest kunnen worden, daar verwacht ik ook geen problemen.

Het laden van schermen wordt op dit moment al gedaan a.d.h.v reflection die gecontroleerd de assembly classes bevatten die een de interface implementeerd maar het framework zal moeten garanderen dat nieuwe versies van de interface niet betekend dat oude versies overboord gegooid wordt. Andere (niet per definitie slechtere) filosofie dan Firefox gebruikt.

De schermen worden apart ontwikkeld door aparte ontwikkel teams die niet bij de sources van het raamwerk code mogen komen en alleen middels de gepubliceerde interface de content mogen ontwikkelen. Zodra de interface aanpast moet er voorkomen worden dat op dat moment enkele tientallen projecten opnieuw opgestart moeten worden omdat deze aangepast moeten gaan worden. Dit is budget technisch niet haalbaar en kost enorm veel additionele test werkzaamheden.

De interface van het framework zal niet erg vaak wijzigen omdat het framework niet zoveel functionaliteiten zelf aanbiedt. De meeste business logic zal namelijk ook middels een plugin systeem geladen worden.

Zal eens kijken naar je suggesties, maar had de vraag misschien wat abstracter bedoeld.

Acties:
  • 0 Henk 'm!

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Op het moment dat je met extensies gaat werken heb je namelijk eigenlijk twee frameworks. Eentje voor de plugin architectuur en een voor hetgeen je eigenlijk wou aanbieden via de extensies.

Je kunt nooit garanderen dat oude extensies blijven werken. Als je dit in het analyze document zet, heb je over 2 jaar zeer grote problemen en moet je in de code allerlei kunstgrepen uithalen om toch backwards compatible te blijven. Kijk om je heen en kijk hoeveel problemen Microsoft heeft om compatible te blijven met eerdere versie van Windows of zelfs MSIE.

Maar stel dat ik een extensie schrijf welke eigenlijk niets van jouw framework gebruikt. Dan zou ik al minimum versie 1.0.0.0 en een maximum versie van 9999.99.99.99. Op dat moment bepaald de extensie dat deze backwards compatible is en wordt dit niet afgedwongen door de framework. Deze manier van werken zie je bijvoorbeeld ook bij de mozilla extensies.

Heb je weleens een website gemaakt? Hou je dan nog steeds rekening met Netscape Navigator 4? Dat is wat 100% backwards compatible inhoud.

Classes en interfaces kunnen veranderen (om bijvoorbeeld nieuwe functionaliteit mogelijk te maken), maar ook database objecten kunnen wijzigen. Beide punten kunnen alleen door de extensie zelf worden ondervangen.

Zoals ik al zei heb je in feite twee frameworks. Het plugin framework zelf kun je inderdaad wel backwards compatible houden, maar niet je functionele framework.

Ga je gebruik maken van signed assemblies? Dan moet je dus zorgen dat alle instanties in een geisoleerd application domain draaien omdat signed assemblies met verschillende versie (framework is versie 1.0.1, maar extentie is gebuild met 1.0.0) niet in dezelfde context kunnen draaien. Je krijgt dan namelijk een assembly version mismatch exception. Dit isoleren van de verschillende extensies dient door je extensie architectuur te gebeuren.

Waarom zouden extensie developers opnieuw moeten beginnen op het moment dat jij een nieuwe versie van het framework released? Als dat zo echt zo is, dan blijft je API gewoon niet constant en kun je dus nooit backwards compabiliteit afdwingen. Het enigste wat een ander development team moet doen is een referentie naar je nieuwe versie maken en opnieuw een build maken. Als de extensie zonder aanpassen gewoon build is de extensie backwards compatible. Moeten de developers op enkele plaatsen kun code wijzigen, dan is het toch logisch dat die extentie nooit op een eerdere versie van jouw framework kan draaien?

En waarom zouden alle ontwikkelteams van de schermen altijd de laatste versie van jouw framework gebruiken? Ik gebruik in mijn projecten libraries en sommig zijn tegen .net 2.0 gecompileerd terwijl andere weer tegen 3.5 zijn gebuild. Het ontwikkelteam van een scherm moet beslissen of zijn naar een nieuwe versie van jouw framework gaan.

Maar heb je al eens naar Microsoft CAB of de [url=http://smartclient.codeplex.com/]Smart Client Software Factory[/code] gekeken? Beide hebben al een extensie framework aan boord (eigenlijk dezelfde omdat SCSF grotendeels op CAB is gebaseerd) en wordt algemene functionaliteit aangebouden in de vorm van services. Van elke service kun je aangeven of deze singleton of transient (per call) is. Ook lazy loading van services (AddOnDemand) is mogelijk. Schemen worden opgebouwd door middel van workspaces (zoals DockWorkspace, TabWorkspace, etc) en daarin worden smartparts (views) geplaatst. Services hebben over het algemeen een interface en een of meerdere implementaties van die interface.

Ik heb heel erg het gevoel dat dit jullie eerste framework is en dat de analyze fase niet zorgvuldig heeft plaats gevonden. Ik zou eens beginnen met zoeken op 'composite (user) interfaces'. Want dat is wat jij nu aan het bouwen bent. SCSF is een goed startpunt voor WinForms applicaties, terwijl Caliburn een goede start is voor WPF. Daarnaast is het denk ik ook verstandig dat je dit artikel van Uncle Bob leest en de eerste vijf principes worden meestal het SOLID principe genoemd.

If it isn't broken, fix it until it is..


Acties:
  • 0 Henk 'm!

  • OMX2000
  • Registratie: Januari 2001
  • Laatst online: 14:21

OMX2000

By any means necessary...

Ik heb de eerste post snel gelezen, en dit riekt naar een Strategy Pattern

In Ruby/Smalltalk zou je het hele probleem niet hebben, aangezien je classes messages ondersteunen. Als een class een message niet ondersteund kun je dat afvangen, en dan eventueel door een andere laten afhandelen.

Dè developers podcast in je moerstaal : CodeKlets Podcast


Acties:
  • 0 Henk 'm!

  • FTL
  • Registratie: Maart 2000
  • Laatst online: 19-09 10:50
@Niemand_Anders:

Er is in dit geval maar een framework die vereist dat de content die er tegenaan wordt gezet alleen een bepaalde interface implementeerd. De content of plugins zoals jij ze noemt, zijn compleet vrij wat ze zelf doen. Geen referenties behalve naar een gemeenschappelijke interface.

Je kunt zeker garanderen dat extensies blijven werken na updaten van interface. Zolang de interface maar goed gedefinieerd en gedocumenteerd is. Er zijn zeer veel voorbeelden op te noemen waarin het wel goed werk in de ICT wereld. Ik noem een microsoft waar je tot voor kort nog gewoon Windows 3.11 applicaties kon starten.

Je schiet echt helemaal door in de redeneringen, ik heb het er niet over dat ze voor eeuwig backwards compatible blijven er zal vast een punt komen waneer er wordt gebroken met compatibility. Maar tot een bepaald punt kunnen we best 100% compatability aanbieden. Zie het meer als de Quirks mode van sommige browsers.

Backwards compatabily zal niet afhankelijk moeten zijn van een database of gewijzigde implementatie die onder in het framework wordt gebruikt, dat zou wijzen op een foutive interface definitie en op een "tight coupling" wat zeker niet zal voorkomen in dit project.

De hele vraag hier ging dus om het idee om een interface versie te kunnen aanbieden die de plugin verwacht. Wanneer de interface verandert zou een adapter pattern aan de framework side het oude gedrag gewoon kunnen aanbieden middels de Observer pattern. Het gedrag is gewoon gedocumenteerd en kan heel goed geautomatiseerd getest worden en ja er zal over een paar jaar best een breaking change kunnen voorkomen. Tot die tijd komt kunnen we wel zorgen dat het blijft functioneren.

De ontwikkelaars hebben niet de keuze of ze een bepaalde versie wel of niet gaan gebruiken, er zal op een server een framework geinstalleerd gaan worden die ondersteunend zal zijn aan meerdere projecten op die server. De verschillende projecten die erop zullen draaien zullen allemaal op een bepaalde versie zijn gebouwd.

Om toch een analogie met de echte wereld te krijgen> je kunt je goed voorstellen dat er binnen in het .NET framework van 2.0 naar 3.5 onder de motorkap bepaalde API's compleet omgegooid worden, maar er wordt nog wel voor een lange periode een (deprecated) API aangeboden zodat programma's geschreven 'op 2.0 api' nog gewoon blijven functioneren op 3.5. We spreken over wijzigingen in het BCL.

Je laatste alinea slaat echt helemaal nergens op, wat weet jij nu wat we al gedaan hebben of niet?
Het tonen van de schermen, business logic, het gedrag en loose coupling met het basis systeem is al geanalyseerd en geïmplementeerd. We hadden hier een van de door jouw (half) fabricaten voor kunnen gebruiken maar dat hebben we niet gedaan .Tevens is het OOD aspect gewoon solide.

De vraag is dus allang niet meer hoe 'plugins' te implementeren maar een OOD design-pattern vraag: hoe kan ik een interface aanbieden waar een class naar vraagt en welke design pattern er voor een een bepaald scenario het beste voor zou zijn. Dus niet over de vraag waarom we backwards compatible willen zijn, niet welke alternatieven er zijn. Gewoon een abstracte vraag.

Acties:
  • 0 Henk 'm!

  • OMX2000
  • Registratie: Januari 2001
  • Laatst online: 14:21

OMX2000

By any means necessary...

Misschien heb je het al gedaan, maar ik zou echt serieus een ontkoppeling maken tussen de berichten/messages die je ondersteund, en de objecten die je messages afhandelen. Dus nogmaals, check de strategy pattern.

Maar misschien hou je liever van lange "educatieve" discussies ;)

Dè developers podcast in je moerstaal : CodeKlets Podcast


Acties:
  • 0 Henk 'm!

  • FTL
  • Registratie: Maart 2000
  • Laatst online: 19-09 10:50
OMX2000 schreef op maandag 15 juni 2009 @ 14:25:
Misschien heb je het al gedaan, maar ik zou echt serieus een ontkoppeling maken tussen de berichten/messages die je ondersteund, en de objecten die je messages afhandelen. Dus nogmaals, check de strategy pattern.

Maar misschien hou je liever van lange "educatieve" discussies ;)
Nee inderdaad. Was vergeten je te bedanken, dat was inderdaad een goede tip!
Zal het zeker meenemen!
Pagina: 1