Programmering

Hvordan man ikke bruger grænseflader i C #

Når du designer et program, skal du ofte bruge grænseflader og abstrakte klasser. Denne artikel diskuterer nogle almindelige eksempler på "grænseflademisbrug" og de strategier, vi kan bruge til at undgå dem. Den diskuterer også, hvad der menes med princippet "program til en grænseflade og ikke til en implementering."

Hvad er grænseflader?

Lad os først forstå en forståelse af grænseflader, og hvorfor de er nødvendige i programmeringen. En grænseflade er strengt taget en kontrakt; det har ingen implementering. En grænseflade indeholder kun medlemserklæringer. Du kan have metodedeklarationer, men ikke definitioner. De medlemmer, der er erklæret i en grænseflade, skal implementeres i de typer (klasser og strukturer), der udvider eller implementerer grænsefladen. En grænseflade kan ikke indeholde felter. En grænseflade kan ikke serialiseres, fordi den ikke kan have data-medlemmer. Som jeg sagde, kan en grænseflade kun have erklæringer og ikke definitioner.

Undgå at foretage ændringer i grænseflader

En klasse eller en struktur, der udvider en grænseflade, skal implementere alle dens medlemmer. Hvis implementeringen ændres, fungerer din kode stadig. Men hvis kontrakten, dvs. grænsefladen, ændres, skal du ændre implementeringerne af alle typer, der udvider grænsefladen. Med andre ord vil enhver ændring af grænsefladen påvirke alle de typer, der udvider grænsefladen. De typer, der udvider grænsefladen, skal overholde kontrakten. Så brug kun grænseflader, når du sjældent har brug for at ændre dem. Det er også generelt bedre at oprette en ny grænseflade end at ændre en eksisterende.

Programmer til en grænseflade, ikke til en implementering

Du har muligvis hørt ordene "program til en grænseflade og ikke til en implementering" nu og da. Du har muligvis brugt grænseflader i din kode, men du programmerede stadig til implementeringen. Lad os nu undersøge forskellen mellem de to tilgange.

Når du programmerer til en grænseflade, bruger du den mest generiske abstraktion (en grænseflade eller en abstrakt klasse) i stedet for en konkret implementering. Da grænseflader garanterer ensartethed, betyder programmering til en grænseflade, at du kan håndtere lignende objekter på en ensartet måde. Dermed afkobles du fra implementeringen - dvs. dine implementeringer kan variere. Dette tilføjer også fleksibilitet til dine designs.

Følgende kodestykke illustrerer programmering til en grænseflade. Overvej en grænseflade ved navn IRepository, der indeholder erklæringen om et par metoder. Klasserne ProductRepository og CustomerRepository udvider IRepository-grænsefladen og implementerer de metoder, der er angivet i IRepository-grænsefladen, som vist nedenfor.

offentlig grænseflade IR-depot

    {

// Noget kode

    }

offentlig klasse ProductRepository: IRepository

    {

// Noget kode

    }

offentlig klasse CustomerRepository: IRepository

    {

// Noget kode

    }

Følgende kode kan bruges til at oprette en forekomst af ProductRepository.

IR depot lager = nyt ProductRepository ();

Ideen er, at du kan bruge enhver klasse her, der implementerer IRepository-grænsefladen. Så følgende erklæring er også gyldig.

IRepository repository = nyt CustomerRepository ();

Når du programmerer til en implementering, går denne ensartethed tabt. I stedet vil du typisk have nogle konstruktioner, såsom “if..else” eller “switch..case” udsagn til styring af adfærd i din kode.

Undgå overforbrug af grænseflader

At knytte hver klasse til en grænseflade er ikke en god praksis. Overbrug af grænseflader på denne måde skaber unødvendig kompleksitet, indfører redundans i kode, krænker YAGNI og reducerer læsbarheden og vedligeholdelsesevnen i kodebasen. Grænseflader bruges til at gruppere objekter, der har samme adfærd. Hvis objekterne ikke har samme adfærd, er der ikke behov for denne gruppering. Brug af grænseflader, når du ikke skal have flere implementeringer af det, er et eksempel på overbrug af grænseflader.

Oprettelse af en grænseflade til en klasse, der matcher de offentlige medlemmer af klassen, er ret almindelig. Ved at gøre dette tilføjer du slet ingen værdi - du duplikerer blot grænsefladen til klassen uden at tilføje nogen reel abstraktion.

Lad os nu se på et eksempel på, hvordan grænseflader bruges for meget. Overvej følgende grænseflade ved navn IProduct.

offentlig grænseflade IProduct

    {

int Id {get; sæt; }

streng Produktnavn {get; sæt; }

dobbelt pris {get; sæt; }

int Mængde {get; sæt; }

    }

Produktklassen udvider IP-produktgrænsefladen som vist nedenfor.

offentlig klasse Produkt: IProduct

    {

public int Id {get; sæt; }

offentlig streng Produktnavn {get; sæt; }

offentlig dobbelt pris {get; sæt; }

offentlig int Mængde {get; sæt; }

    }

Det er klart, at vi ikke har brug for IProduct-grænsefladen, da grænsefladen og dens implementering er identisk. Den overflødige kode er unødvendig.

Lad os se på et andet eksempel. Følgende kodestykke viser en grænseflade ved navn IProductManager, der har deklarationen af ​​to metoder, nemlig Gem og opdater.

 offentlig grænseflade IProductManager

    {

ugyldig Gem (IProduktprodukt);

ugyldig opdatering (IP-produkt)

    }

IProductManager-grænsefladen indeholder erklæringerne om de offentlige metoder i ProductManager-klassen. Sådan ser ProductManager-klassen ud.

 offentlig klasse ProductManager: IProductManager

    {

offentlig tomrum Gem (IProduktprodukt)

        {

// Skriv din implementering her

        }

offentlig ugyldig opdatering (IProduct produkt)

        {

// Skriv din implementering her

        }

    }

Grænsefladerne IProduct og IProductManager er eksempler på overbrug af interface. Begge disse grænseflader har en enkelt implementering, og de tilføjer slet ingen værdi.

Ved at bruge grænseflader kan du fjerne de unødvendige koblinger i din kode og gøre din kode let testbar. Imidlertid bør overforbrug af grænseflader undgås. Brug kun grænseflader, når der vil være mere end en implementering af dem. Du kan også bruge grænseflader, når du har en klasse, der har mange roller at spille, eller som har flere ansvarsområder. I dette tilfælde kan din klasse implementere flere grænseflader - en til hver rolle.