Programmeertaal trade-offs

Link: https://www.garfieldtech.com/blog/language-tradeoffs

Dit artikel is ook beschikbaar in de het Servo-kroatisch

Er is veel discussie laat in Drupal over Object-Georiënteerd Programmeren. Dat is ook niet verwonderlijk, gezien het feit dat Drupal 7 is de eerste versie die heeft echt geprobeerd om het gebruik van objecten in een zinvolle manier (vis, als iets anders dan matrices die pass vreemd). Echter, te veel van de discussie neer op “OMG objecten zijn flexibel, zodat ze kwaad!” vs. “OMG objecten zijn cool, yay!” Beide posities zijn schadelijk naïef.

Het is voor ons belangrijk om een stap terug te doen en te onderzoeken waarom een bepaalde programmering paradigma is nuttig, en om dat te doen moeten we begrijpen wat we bedoelen met “nuttig”.

Programmeerparadigma ‘ s, zoals software-architectuur, trade-offs. In feite, veel van de methoden voor het vergelijken van bouwkundige ontwerpengelden net zo goed voor taal-ontwerp. Om dat te doen, hoewel, we moeten een stap terug en kijk naar meer dan alleen PHP-stijl objecten.

Waarschuwing: Hard-core computer science actie volgt. Als je een coder, ik aanraden om een kopje $drank alvorens verder te gaan, als het zou kunnen nemen een beetje te verteren, hoewel ik heb geprobeerd het te vereenvoudigen en zo veel mogelijk. Er is vrij weinig Drupal-specifieke dingen hier dus hopelijk zou het nuttig zijn om een PHP ontwikkelaar.

Benaderingen te programmeren

Elke programmeer taal is in wezen een manier van coderen van logica. Er zijn verschillende was te coderen logica op een manier die een computer kan verwerken, en elke taal heeft een iets andere draai aan het onderwerp. In het abstracte, maar we kunnen identificeren verschillende algemene paradigma ‘ s, een aantal die relevant zijn voor ons als PHP ontwikkelaars. (De Wikipedia links hieronder hebben veel meer details dan ik kan gaan hier.)

Procedureel programmeren
Procedurele is vaak de eerste programmering stijl geleerd. Een programma is georganiseerd in de “procedures” (aka functies of subroutines) die dan betrekking op de gegevens. De code wordt over het algemeen geschreven in een dwingende mode: X, doe dan Y, dan doen Z. Deze subroutines hebben vaak bijwerkingen; sommige programma staat is permanent veranderd op een manier die duurt dan het leven van de subroutine.
Functioneel programmeren
Functioneel programmeren is net zo oud als procedureel programmeren. In feite, de twee benaderingen werden de oorspronkelijke schisma in de programmering van de conceptualisering. Puur functioneel programmeren van een niet schrijft een aantal stappen voor het programma te volgen. In plaats daarvan schrijft wiskundige functies die aan elkaar gerelateerd zijn. Dat heeft een aantal belangrijke kenmerken: met name het puur functionele programma ‘ s zijn niet in staat de bijwerkingen. Functies hebben geen toestand van hun eigen, en in feite wanneer een variabele een waarde heeft kan niet meer veranderd worden. Ooit. De output van een functie, hangt uitsluitend af van de expliciete ingangen. In het algemeen, functionele talen ook de behandeling van functies als eerste-klas-objecten langs de kant andere, meer bekende variabelen als ints, touwtjes, enzovoort.
Declaratieve programmering
Declaratieve talen voorkomen te geven hoe de computer moet iets doen in het voordeel van te zeggen wat hij moet doen, een of andere manier. Dit is een extreem krachtig voor normale taken eenvoudiger, maar kan de ontwikkeling van nieuwe en innovatieve mogelijkheden moeilijker; ze moeten nog steeds worden vertaald uit het declaratieve vorm in een uitvoerbare aanpak ergens. SQL zelf is de meest voor de hand liggende voorbeeld voor web-ontwikkelaars, maar er zijn er veel meer. Zelfs sommige markup talen kan misschien wel worden beschouwd als een declaratieve programmering. (Zie ook: HTML5 of SVG)
Klassieke Object-Georiënteerd programmeren
In klasse-gebaseerde OO (of “Classic” als ik de neiging om het te noemen), in plaats van logica die is ingekapseld in eenvoudige subroutines die actief zijn op willekeurige gegevens worden doorgegeven logica en data zijn gebundeld in ‘objecten’. Deze objecten worden behandeld door de buitenwereld als een enkele zwarte doos. Manipulatie van de gegevens gebeurt niet rechtstreeks, maar gebeurt door de logica is gebonden aan de gegevens. Dat is, via methoden van het object. Populaire voorbeelden hiervan zijn C++, Java en PHP.
Prototypische OO programmeren
Dit klasseloze variant van OO is in sommige opzichten dichter bij functioneel programmeren. Javascript is het meest bekende voorbeeld van deze stijl. Want het is niet echt mogelijk in PHP laten we het voor het moment. Ik scheiden van de “Klassieke” OO vooral op wijzen dat het PHP-benadering tot OO is niet universeel.
Aspect-georiënteerd programmeren
Een relatieve nieuwkomer op het toneel, AOP is gebaseerd op het creëren van join punten tussen de verschillende delen van een systeem. Dat is de plek waar de logische routine kan injecteren zelf zijn in de andere zonder te wijzigen routine direct.

(Opmerking: ik ben er zeker van dat sommige puristen zullen zeggen dat ik grove over-vereenvoudiging van één of meer van de bovengenoemde stijlen. Ze zijn waarschijnlijk gelijk, maar omwille van het argument, ik ben alleen op zoek naar enkele aspecten van deze benaderingen. Als u wilt dat een meer volledige behandeling, dat is wat de links zijn. Ik raad ze te lezen met een open geest.)

Er is een zeer belangrijk feit over de bovengenoemde ontwerpen, dat is belangrijk om in gedachten te houden: Ze zijn gelijkwaardig. Het is wiskundig bewezen dat procedurele en functionele talen zijn even expressief; dat is, is een algoritme die u kunt implementeren in één geïmplementeerd kunnen worden in de andere. OOP en AOP zijn in wezen gewoon uitwassen van procedureel programmeren en vaak zijn uitgevoerd in multi-paradigma talen, dus alles wat je kunt doen in een procedurele taal kan worden gedaan in een OOP taal of AOP taal, of vice-versa. Declaratieve de programmering is de odd man out zo veel declaratieve talen niet turing-compleet is, hoewel veel als je er hard genoeg.

Dus als al deze programmeerparadigma ‘ s zijn gelijk, waarom zou je met behulp van de een over de ander?

Taal focus

Simpel: Elke benadering heeft verschillende trade-offs en dat maakt het makkelijker of moeilijker te schrijven van bepaalde vormen van algoritmen. Niet “mogelijk”; Alle functionaliteit die u kunt implementeren in functionele talen kunnen worden geïmplementeerd in een aspect-georiënteerde taal, en vice-versa. Ze maken het “makkelijker”. Het bedrag van de code en het bedrag van de onbegrijpelijke complexiteit zal sterk variëren. Wat maakt verschillende paradigma ‘ s gemakkelijker toe te passen op bepaalde algoritmen? Wat ze niet laten doen.

Dat klopt. Wat maakt een programmeerstijl gemakkelijker is wat het laat je niet doen. Want als je weet voor een feit dat bepaalde dingen nietmogelijk is, kunt u het maken van veronderstellingen die zijn gebaseerd op de onmogelijkheid die andere taken eenvoudiger.

Functioneel: de Logica centric

Bijvoorbeeld, in een puur functionele talen je weet voor een feit dat dezelfde set van input voor een gegeven functie altijd hetzelfde resultaat. Dat betekent dat de compiler zelf kunt optimaliseren afstand meerdere oproepen naar dezelfde functie met dezelfde parameters. Ga je gang en aanroepen van een functie meerdere keren. Niet de moeite caching het resultaat. De taal semantiek zelf zal dat voor u doen zonder enige gedachte. Pulseaudio, geloof ik, precies dat doet. Het maakt ook de Controleerbaarheid gemakkelijk; u kunt een wiskundig bewijs van de juistheid van een bepaalde functie onafhankelijk van de rest van het systeem. Dat kan heel nuttig zijn in een schuld-intolerant systeem, zoals, zeg, nucleaire reactoren of air traffic control waar bugs worden echt gevaarlijk.

Omdat u weet voor een feit, dat een functie heeft geen invloed op de code buiten zichzelf (los van de return waarde), er is geen vereiste dat een functie toegang hebben tot alle gegevens, behalve zijn eigen parameters. Het hoeft zelfs niet in dezelfde ruimte in het geheugen als de rest van het programma… of zelfs op dezelfde computer. Getuige Erlang, waarbij elke functie kan worden uitgevoerd in een eigen thread, of zijn eigen proces, of zelfs in een proces op een andere computer zonder syntactische wijzigingen. Dat maakt programma ‘ s geschreven in Erlang extreem verspreiden en schaalbaar, juist omdat de functies niet in staat zijn tot het produceren van kant-effecten.

Natuurlijk, voor een aantal use-cases de programma structuur functionele talen wordt vreselijk smerig. Dat geldt met name voor programma ‘ s die zijn gebaseerd op het manipuleren van state over een lange termijn. Dat is de trade-off: een helder, logisch eenvoudige structuur die maakt complexe algoritmen gemakkelijk op te bouwen recht en schalen goed, maar maakt stateful systemen moeilijker te bouwen.

Procedurele: Instructie centric

Procedurele programma ‘ s waren de andere grote vork in de programmeertaal theorie. Procedureel programmeren kan beginnen heel simpel als een lijst van instructies, gebroken in stukken (subroutines en functies). Ook is de globale staat in een of andere vorm in aanvulling op een lokaal bereik staat.

Wat procedurele talen niet laten doen is zwaar segment van uw programma. Er is een groot zwembad van subroutines die worden aangeroepen vrijwel op elk moment. U kan zich niet binden een bepaalde subroutine tot bepaalde gegevens of vice versa. Dat maakt de code onvoorspelbaar is, als je functie kan worden aangeroepen vanuit heel letterlijk overal en op elk moment. U kunt geen veronderstellingen maken over het milieu, je loopt in, vooral als uw systeem maakt gebruik van globale variabelen (of hun nauwe neef, statische variabelen). Er is geen manier om te verbergen van gegevens, is er geen manier om te bepalen wanneer een bepaalde routine of niet kan worden uitgevoerd, is er geen manier om jezelf te beschermen tegen een andere ontwikkelaar te hacken zijn weg in een subroutine die niet ontworpen zijn om gehackt te worden.

Dat is natuurlijk ook de kracht. Omdat het zo laag niveau en heeft geen beveiliging, u kunt hacken van uw weg in (of buiten) de meeste situaties met voldoende inspanning. Omdat je verhinderd voor het verbergen van gegevens, krijg je een grote mate van flexibiliteit. Dat kan heel goed in een aantal gevallen. Aan de andere kant, dat betekent dat in het algemeen procedureel programmeren kun je geen aannames over de context van uw systeem of haar staat.

Omdat je zo weinig controle, het is uiterst moeilijk om een zinvolle vorm van unit testing. U kunt doen het functionele testen (testen van functionaliteit op een hoog niveau) of de integratie testen, maar je hebt geen duidelijk te onderscheiden eenheden te werken.

Object-oriented Gedrag centric

Er zijn veel variaties op object-georiënteerde talen, elk met hun eigen subtiliteiten. Voor het moment zijn we bezig alleen met Klas-en-Interface talen zoals PHP. Het deel van de interface is belangrijk: In een Klassieke OO taal, individuele primitieve waarden zijn niet relevant. De interface van een object, zoals gedefinieerd door de klasse en interfaces, is wat telt. De klas vormt een volkomen nieuwe gegevens type met een eigen semantiek.

Net als een string primitieve heeft de semantiek (bijv. lengte) en mogelijke operaties (splitsen, samenvoegen, etc.), zo heeft ook een object. De interne implementatie van de string is niet relevant: het kan een hash-tabel, is het mogelijk een rechte karakter array. Als iemand gebruik te maken van het u niet kent, noch kan het jou schelen (behalve in C, natuurlijk). Ook met een object, is het gedrag zoals gedefinieerd door de methoden. De onderliggende implementatie is niet relevant.

De gegevens binnen de klas is nauw gekoppeld aan de klasse, de klasse zelf (indien correct gedaan) losjes gekoppeld aan iets anders. Gegevens binnen de klasse, is het irrelevant om het even wat, maar die klasse. Omdat het verborgen (“ingekapseld” in de academische lingo), u weet voor een feit, dat alleen geselecteerde stukjes code (in dezelfde klasse) zijn in staat om het te wijzigen. In tegenstelling tot de procedurele code, kunt u vertrouwen op de gegevens niet wijzigen onder je. U kunt zelfs helemaal herstructurering van de code. Zolang de interface niet verandert, is dat het probleem, je bent goed.

Dat is een belangrijk onderscheid. In OO, u bent niet de codering van de gegevens. U bent codering is aan het gedrag. Data is ondergeschikt aan het gedrag van een object.

Omdat u hebt geïsoleerd gegevens achter gedrags-muren, kunt u controleren en de unit test elke klasse afzonderlijk. Dat is, in de veronderstelling dat heb je goed geïsoleerd object. Veel van de code niet goed doen, die nederlagen het doel. (Zie mijn vorige rants op dependency injection.)

Aspect-georiënteerd: Side-effect centric

En ten slotte komen we tot nieuwe jongen op het blok Aspect-georiënteerd programmeren. In sommige opzichten, AOP is de ordinaten tegenovergestelde van functioneel programmeren. Waar functioneel programmeren probeert te elimineren bijwerkingen, AOP is gebaseerd op hen. Elke join-punt in de AOP is een grote rode vlag te zeggen “doe bijwerkingen hier”. Deze bijwerkingen kunnen worden allerlei dingen. Ze kon het wijzigen van gegevens, ze kon wijzigen van het programmaverloop, kunnen ze starten een aantal andere zijband logica en zelfs leiden tot verder side-effecten.

Wat AOP biedt, is precies dat: De mogelijkheid om een programma te veranderen zonder het wijzigen van een programma. Zodra een join-point is vastgesteld, dan kunt u wijzigen in de gegevens of programma-logica op dat moment zonder het wijzigen van een bestaande code. Dat biedt een grote flexibiliteit en uitbreidbaarheid, maar ten koste van de controle.

Zodra je de invoering van een manier om 3rd party code te wijzigen in uw logica gegevens of stroom, geef je je over een mogelijkheid om te controleren dat de logica gegevens of stroom. Kunt u geen gebruik meer maken van veronderstellingen over je staat, want je hebt opgebouwd in een mechanisme om uw status te wijzigen onder je. De manier waarop je compartimentaliseren uw code te maken het onmogelijk om volledig te compartimentaliseren van uw code. (Denken dat het een voor een moment…)

Trade-offs

Functionele benaderingen benadrukken de Controleerbaarheid, Testability en Schaalbaarheid ten koste van de Ontwikkeling, de Uitbreidbaarheid, en in sommige gevallen Begrijpelijkheid.

Procedurele benaderingen benadrukken Ontwikkeling, Begrijpelijkheid, en de Snelheid ten koste van Testability, Controleerbaarheid, en als je niet voorzichtig bent Onderhoudbaarheid.

Object-georiënteerde benaderingen benadrukken Testability, Ontwikkeling en Schaalbaarheid ten koste van Uitbreidbaarheid, Opportuniteit, en als je een slecht ontwerp Begrijpelijkheid.

Aspect-georiënteerde benaderingen benadrukken Ontwikkeling, Uitbreidbaarheid en de Snelheid ten koste van Testability, Controleerbaarheid, en misschien wel Begrijpelijkheid.

Oh geweldig, dus welke willen we gebruiken? Welke aanpak is de beste? De een die het beste past bij uw gebruik van het geval en prioriteiten, natuurlijk.

Multi-paradigma talen

Omdat al deze benaderingen zijn perfect haalbaar, afhankelijk van uw gebruik van het geval, het meest belangrijke programmeertalen vandaag zijn multi-paradigma. Dat is, zij ondersteunen, tenminste tot op zekere hoogte, meerdere benaderingen en manieren van denken over de programma-logica.

PHP begon het leven als een geheel van procedurele taal. Met PHP 4 het begon met het toevoegen van object-georiënteerde mogelijkheden, hoewel die niet echt tot hun recht komen als een levensvatbaar alternatief tot PHP 5. PHP 5.3 geïntroduceerd anonieme first-class functies, die, hoewel niet zuiver functioneel programmeren, omdat ze nog steeds mogelijk variabelen te wijzigen toestaan programmeurs die zijn zo geneigd om te schrijven in een meer functionele manier.

Hoewel de meeste aspect-georiënteerde implementaties zijn gebouwd bovenop object-georiënteerde modellen, PHP ondersteunt procedurele op basis van AOP. In Drupal, noemen we het haken. module_invoke_all() wordt een gezamenlijk punt, en een haak uitvoering wordt een pointcut.

(Ik ben niet de eerste om te bellen Drupal haak systeem een vorm van AOP. Ik denk dat het een bijzonder goede manier om ze te omschrijven.)

Om eerlijk te zijn, zonder eigen syntactische ondersteuning haken zijn een nogal lompe, gehackt-up poor man ‘ s AOP, maar conceptueel is het nog steeds AOP. Ze hebben dezelfde impliciete trade-offs: Zeer flexibel bij het op de juiste wijze gebruikt, maar volledig vernietigen hoop van het isoleren van een systeem om de unit te testen of de interface-driven development.

Het feit dat ze ook vastgeschroefd op de top van een niet-AOP taal, maar niet gedocumenteerd als zijnde AOP, of consequent toegepast, is ook een de grote struikelblok voor nieuwe ontwikkelaars, vooral degenen die zijn opgegroeid in een overwegend OO wereld.

Net zoals het mogelijk is om te emuleren AOP in procedurele code, is het mogelijk in de object-georiënteerde code. Er zijn vele OOP patronen die geven je alle dezelfde flexibiliteit als AOP, bijvoorbeeld, en soms meer uitgebreide manieren. Waarnemer en Bezoekers patronen komen voor de geest in het bijzonder. Nogmaals, het is niet een kwestie van kunnen u bij het implementeren van een bepaald design, maar hoe makkelijk je kunt het doen, en tegen welke kosten.

Niets verbiedt het mixen en matchen van verschillende benaderingen. Neem Drupal 7 de Database layer. Het is meestal recht omhoog OO — Modulaire, afhankelijkheid ingespoten, self-contained, interface-gedreven — maar gooit in sommige AOP in de vorm van hook_query_alter() en procedurele gemak wrappers, zoals db_query(). Ik zeker niet beweren dat het een perfecte balans, maar het laat wel zien hoe meerdere benaderingen kan worden gebruikt samen.

de Beslissingen, beslissingen

Bij het overwegen van het aanpakken van een bepaald probleem, of het gebruik van een bepaalde taal hebben, het is niet genoeg om te zeggen “ik hou van X” of “benadering” Y is stom”. Dat is een naïeve benadering en heeft de neiging te leiden tot spaghetti-code. (Pasta bestaat in alle talen.) In plaats daarvan moeten we vragen wat onze prioriteiten zijn, waar wij bereid bent op te geven, en wat we bereid zijn te doen om te verminderen. Wij hebben altijd tot iets. Altijd.

Welke kosten u wilt betalen, is niet altijd een eenvoudige balans te slaan. Doe je voordeel robuustheid (Testability, Controleerbaarheid, de Schaalbaarheid, het verbergen van data, encapsulation, enz.), flexibiliteit (Ontwikkeling, Uitbreidbaarheid, bare gegevens, enz.) of eenvoud (Opportuniteit, Onderhoudbaarheid, eventueel Prestaties, enz.)?

Kies twee.

met dank aan Bec Wit en Matt Farina voor hun inbreng op dit artikel.

Leave a Reply