Beregnet lesetid: 7 minutter
Å designe programvaren: moduler, klasser og funksjoner på en slik måte at når vi trenger ny funksjonalitet, bør vi ikke endre eksisterende kode, men heller skrive ny kode som skal brukes av eksisterende kode. Dette kan virke rart, spesielt med språk som Java, C, C ++ eller C # hvor det ikke bare gjelder kildekoden selv, men også binæren. Vi ønsker å lage nye funksjoner på måter som ikke krever omfordeling av eksisterende binære filer, kjørbare filer eller DLL-filer.
OCP i SOLID-sammenheng
Vi har allerede sett SRP-prinsippet om enkeltansvar som sier at en modul bare skal ha en grunn til å endre. OCP- og SRP-prinsippene er komplementære. Koden utformet etter SRP-prinsippet vil også respektere OCP-prinsippene. Når vi har kode som bare har én grunn til å endre, vil innføring av en ny funksjon skape en sekundær årsak til den endringen. Så både SRP og OCP ville bli brutt. På samme måte, hvis vi har kode som bare skal endres når hovedfunksjonen endres og skal forbli uendret når ny funksjonalitet er lagt til, og dermed respektere OCP, vil den også respektere SRP.
Dette betyr ikke at SRP alltid fører til OCP eller omvendt, men hvis en av dem overholdes, er det ganske enkelt å oppnå det andre.
Rent teknisk sett er det åpne / lukkede prinsippet veldig enkelt. Et enkelt forhold mellom to klasser, som den nedenfor, bryter OCP-prinsippet.
Brukerklassen bruker Logic-klassen direkte. Hvis vi trenger å implementere en annen Logic-klasse på en måte som lar oss bruke både den nåværende og den nye, må den eksisterende Logic-klassen endres. Brukeren er direkte knyttet til implementeringen av logikken, det er ingen måte for oss å gi ny logikk uten å påvirke den nåværende. Og når vi snakker om statiske typede språk, vil brukerklassen sannsynligvis også kreve endringer. Hvis vi snakker om kompilerte språk, vil helt sikkert både kjørbar bruker og Logic kjørbar eller det dynamiske biblioteket kreve rekompilering og levering, helst å unngå når det er mulig.
Med henvisning til forrige opplegg kan vi utlede at enhver klasse som direkte bruker en annen klasse, kan føre til brudd på Open / Closed-prinsippet.
La oss anta at vi ønsker å skrive en klasse som kan gi fremdriften "i prosent" av en nedlastet fil, gjennom applikasjonen vår. Vi vil ha to hovedklasser, en fremgang og en fil, og jeg antar at vi vil bruke dem som følger:
function testItCanGetTheProgressOfAFileAsAPercent() {
$file = new File();
$file->length = 200;
$file->sent = 100;
$progress = new Progress($file);
$this->assertEquals(50, $progress->getAsPercent());
}
I denne koden er vi Progress-brukere. Vi ønsker å få en verdi i prosent, uavhengig av den faktiske filstørrelsen. Vi bruker File som en kilde til informasjon. En fil har en lengde i byte og et felt kalt sendt som representerer mengden data sendt til nedlasteren. Vi bryr oss ikke om hvordan disse verdiene oppdateres i applikasjonen. Vi kan anta at det er noen magisk logikk som gjør dette for oss, så i en test kan vi eksplisitt sette dem.
class File {
public $length;
public $sent;
}
Filklassen er bare et enkelt dataobjekt som inneholder de to feltene. Selvfølgelig bør den også inneholde annen informasjon og atferd, for eksempel filnavn, bane, relativ bane, gjeldende katalog, type, tillatelser og så videre.
class Progress {
private $file;
function __construct(File $file) {
$this->file = $file;
}
function getAsPercent() {
return $this->file->sent * 100 / $this->file->length;
}
}
Fremgang er ganske enkelt en klasse som godtar en fil i konstruktøren. For klarhetens skyld har vi spesifisert variabeltypen i konstruktorparametrene. Det er en enkelt nyttig metode på Progress, getAsPercent (), som tar de sendte verdiene og lengden fra File og gjør dem til en prosentandel. Enkelt og det fungerer.
Denne koden ser ut til å være riktig, men den bryter med Open / Closed-prinsippet.
Men hvorfor?
Og hvordan?
Hver applikasjon trenger nye funksjoner for å utvikle seg over tid. En ny funksjon for applikasjonen vår kan være å tillate streaming av musikk i stedet for bare å laste ned filer. Lengden på filen er representert i byte, hvor lenge musikken varer i sekunder. Vi ønsker å tilby en fremdriftslinje til lytterne våre, men kan vi bruke klassen som er skrevet ovenfor?
Nei vi kan ikke. Vår progresjon er bundet til File. Den kan bare administrere filinformasjon, selv om den også kan brukes på musikkinnhold. Men for å gjøre det må vi endre det, vi må gjøre Progress kjent med musikken og filene. Hvis designet vårt overholdt OCP, trenger vi ikke å trykke på File eller Progress. Vi kan bare gjenbruke eksisterende fremgang og bruke den på musikk.
Dynamisk typte språk har fordelen av å administrere objekttyper i løpetid. Dette lar oss fjerne typehint fra Progress-konstruktøren, og koden vil fortsette å fungere.
class Progress {
private $file;
function __construct($file) {
$this->file = $file;
}
function getAsPercent() {
return $this->file->sent * 100 / $this->file->length;
}
}
Vi kan nå lansere hva som helst på Progress. Og med noe, mener jeg bokstavelig talt hva som helst:
class Music {
public $length;
public $sent;
public $artist;
public $album;
public $releaseDate;
function getAlbumCoverFile() {
return 'Images/Covers/' . $this->artist . '/' . $this->album . '.png';
}
}
Og Musikk-klassen som den ovenfor vil fungere perfekt. Vi kan enkelt teste den med en test som ligner på File.
function testItCanGetTheProgressOfAMusicStreamAsAPercent() {
$music = new Music();
$music->length = 200;
$music->sent = 100;
$progress = new Progress($music);
$this->assertEquals(50, $progress->getAsPercent());
}
Så i utgangspunktet kan ethvert målbart innhold brukes med Progress-klassen. Kanskje vi bør uttrykke det i kode ved å også endre variabelnavnet:
class Progress {
private $measurableContent;
function __construct($measurableContent) {
$this->measurableContent = $measurableContent;
}
function getAsPercent() {
return $this->measurableContent->sent * 100 / $this->measurableContent->length;
}
}
Da vi spesifiserte File som typehint, var vi optimistiske med hensyn til hva klassen vår takler. Det var eksplisitt, og hvis noe annet kom, ville en stor feil fortelle oss.
Una klasse som overstyrer en metode for en basisklasse slik at baseklassekontrakten ikke blir respektert av den avledede klassen.
Vi vil ikke ende opp med å prøve å ringe metoder eller få tilgang til felt på objekter som ikke er i samsvar med kontrakten vår. Da vi hadde en typetips, ble kontrakten spesifisert av den. Feltene og metodene i filklassen. Nå som vi ikke har noe, kan vi sende noe, til og med en streng, og det vil føre til en feil.
Mens sluttresultatet er det samme i begge tilfeller, noe som betyr at koden går i stykker, ga førstnevnte en hyggelig melding. Dette er imidlertid veldig mørkt. Det er ingen måte å vite hva variabelen er – en streng i vårt tilfelle – og hvilke egenskaper som ble søkt etter og ikke funnet. Det er vanskelig å feilsøke og fikse problemet. En programmerer må åpne Progress-klassen, lese og forstå den. Kontrakten, i dette tilfellet, når du ikke eksplisitt spesifiserer typehintet, er defiavsluttet av fremdriftens oppførsel. Det er en underforstått kontrakt kun kjent for Progress. I vårt eksempel er det det defiavsluttes ved å få tilgang til de to feltene, sendt og lengde, i getAsPercent()-metoden. I det virkelige liv kan den underforståtte kontrakten være svært kompleks og vanskelig å oppdage ved bare å se etter noen få sekunder i timen.
Denne løsningen anbefales bare hvis ingen av de andre tipsene nedenfor lett kan implementeres, eller hvis de vil påføre alvorlige arkitektoniske endringer som ikke garanterer innsatsen.
Continua leggendo il terzo principio della sostituzione di Liskow —>
Ercole Palmeri
Begrepet bærekraft er nå mye brukt for å indikere programmer, initiativer og handlinger som tar sikte på å bevare en bestemt ressurs...
Enhver virksomhet produserer mye data, selv i forskjellige former. Skriv inn disse dataene manuelt fra et Excel-ark for å...
Kompromisset med bedrifts-e-poster økte mer enn det dobbelte i de tre første månedene av 2024 sammenlignet med siste kvartal av...
Prinsippet for grensesnittsegregering er ett av de fem SOLID prinsippene for objektorientert design. En klasse skal ha...
Microsoft Excel er referanseverktøyet for dataanalyse, fordi det tilbyr mange funksjoner for å organisere datasett,...
Walliance, SIM og plattform blant de ledende i Europa innen eiendoms Crowdfunding siden 2017, kunngjør fullføringen...
Filament er et "akselerert" Laravel-utviklingsrammeverk, som gir flere fullstack-komponenter. Den er designet for å forenkle prosessen med...
«Jeg må tilbake for å fullføre utviklingen min: Jeg vil projisere meg selv inne i datamaskinen og bli ren energi. En gang bosatt seg i…