GPT 4 tentafrågor - Dependency injection & strategy pattern Flashcards
Vad är huvudsyftet med Dependency Injection i mjukvarudesign?
a) Att öka prestanda under runtime.
b) Att tillåta objekt att skapa sina egna beroenden.
c) Att separera objekts skapande från deras användning, öka underhållbarheten och flexibiliteten.
Rätt svar: c
Förklaring: Genom att “injicera” beroenden istället för att “konstruera” dem i klasser, separerar Dependency Injection objekts skapande från deras användning. Detta ökar underhållbarheten och flexibiliteten i kodbasen, vilket gör det lättare att ändra beroenden utan att modifiera klasserna själva.
Hur skulle du använda Dependency Injection i kontexten av en spelkaraktär som kan utföra olika typer av hopp?
a) Skapa en Player-klass som själv beslutar vilket hoppbeteende som ska användas.
b) Använda en IJumpBehavior-interface och injicera konkreta beteenden i Player-klassen vid behov.
c) Låta spelet globalt bestämma vilket hoppbeteende alla spelare ska ha.
Rätt svar: b
Förklaring: Genom att använda ett IJumpBehavior-interface kan olika hoppbeteenden implementeras som separata klasser. Dessa beteenden kan sedan injiceras i Player-klassen, vilket gör det möjligt att enkelt byta hoppbeteende under spelets gång.
Vilken typ av Dependency Injection används i exemplet där en Player-klass tar emot ett IJumpBehavior-objekt genom sin konstruktor?
a) Constructor Injection
b) Method Injection
c) Property Injection
Rätt svar: a
Förklaring: Constructor Injection innebär att beroenden levereras via klassens konstruktor. Detta är lämpligt när ett beroende är obligatoriskt för klassens funktion, som i fallet med IJumpBehavior i Player-klassen.
Vilket påstående är sant om Dependency Injection?
a) Det minskar kodens flexibilitet eftersom det lägger till fler beroenden.
b) Det är endast användbart när objekt har många beroenden.
c) Det underlättar ändringar i beteende vid runtime genom att tillåta att beroenden kan bytas ut.
Rätt svar: c
Förklaring: Ett av de primära syftena med Dependency Injection är att öka kodens flexibilitet och underhållbarhet. Genom att injicera beroenden, som kan bytas ut vid behov, kan systemets beteende enkelt ändras vid runtime.
Betrakta följande kodsegment. Vilket är det bästa sättet att tillämpa Dependency Injection här?
class Player { public void Jump() { // Antag att det här är standardhoppbeteendet. Console.WriteLine("Performing regular jump."); } }
a) Ändra Jump-metoden i Player-klassen varje gång ett nytt hoppbeteende introduceras.
b) Skapa ett IJumpBehavior-interface och låt Player-klassen hålla en referens till ett IJumpBehavior-objekt som bestämmer hur man hoppar.
c) Låta en global inställning bestämma vilket hoppbeteende som ska användas i Jump-metoden.
Rätt svar: b
Förklaring: Att ha ett IJumpBehavior-interface tillåter Player-klassen att delegera hoppbeteendet till det objekt som implementerar interfacet. Detta separerar hopplogiken från Player-klassen och gör det enkelt att införa nya hoppbeteenden utan att ändra Player-klassen.
Which statement is correct regarding the Strategy pattern?
- The Strategy pattern requires using inheritance to switch behaviors at runtime.
- In the Strategy pattern, it’s impossible to add new strategies without modifying existing classes.
- The Strategy pattern encapsulates specific behaviors or algorithms into separate classes, making them interchangeable.
The correct answer is option 3: “The Strategy pattern encapsulates specific behaviors or algorithms into separate classes, making them interchangeable.”
The Strategy pattern is all about enabling an object, often known as the context, to vary its behavior based on its strategy object. Strategies are a set of algorithms that can be swapped at runtime as per the needs. The encapsulation of these algorithms (as individual strategies) avoids code duplication and promotes a high degree of cohesion and low coupling. The other options are incorrect because the Strategy pattern doesn’t rely on inheritance for swapping behaviors and is designed specifically to avoid having to modify existing code to add new behaviors.
In the context of the Strategy pattern, which class or interface is responsible for defining a family of algorithms?
- ConcreteStrategy classes
- Context class
- IStrategy interface (or similarly named interface)
The correct answer is option 3: “IStrategy interface (or similarly named interface).”
In the Strategy pattern, the “IStrategy” interface defines a common platform for all concrete strategies, ensuring each strategy conforms to a specific blueprint (i.e., having certain methods that need to be implemented). The “Context” class maintains a reference to a strategy object, which represents an interface common to all supported algorithms. ConcreteStrategy classes are merely the implementations of these algorithms. The interface is central because it makes the Context class and the concrete strategies loosely coupled.
Consider the following code snippet:
public class Character { private IWeaponBehavior weapon; public Character(IWeaponBehavior weapon) => this.weapon = weapon; public void Attack() => weapon.UseWeapon(); }
Which principle is demonstrated in the code above?
- Inheritance over composition
- Composition over inheritance
- Static binding over dynamic binding
The correct answer is option 2: “Composition over inheritance.”
The provided code snippet demonstrates the “Composition over inheritance” principle. This principle suggests that a class should achieve polymorphic behavior and code reuse by containing instances of other classes that implement the desired functionality, rather than inheriting from a base or parent class. In this example, the Character class doesn’t inherit methods from the IWeaponBehavior classes. Instead, it has a weapon (IWeaponBehavior) property, allowing it to delegate the Attack method’s behavior to the encapsulated “weapon” object, leading to more flexibility and the possibility to change behaviors at runtime.
In the sample code where different weapons implement IWeaponBehavior, how does this design allow for flexibility?
- By allowing the Character class to directly instantiate each weapon it needs.
- By using a common interface, enabling the Character class to interact with different weapon strategies dynamically.
- By limiting each Character to a single type of weapon to avoid complexity.
The correct answer is option 2: “By using a common interface, enabling the Character class to interact with different weapon strategies dynamically.”
The design is flexible because the IWeaponBehavior interface allows the Character object to switch its weapon (strategy) at runtime, as it doesn’t instantiate weapon objects directly. This approach means that if we introduce new weapons (concrete strategies), the Character class doesn’t need to change. The Character can use any weapon (strategy) as long as the weapon adheres to the IWeaponBehavior interface, highlighting the interoperability and flexibility provided by using a common interface. The Character’s behavior with regards to its weapon can change dynamically because it’s based on the object’s composition (which weapon it’s composed with) rather than the class inheritance hierarchy.
Vad är sant om Strategy-mönstret?
1. En konkret strategi-klass kan ändra sitt beteende vid körtid.
2. Strategi-gränssnittet kan ändra beteende vid körtid.
3. Klassen som använder en strategi får ett beteende som beror på den valda
strategin.
Rätt svar: 3. Klassen som använder en strategi får ett beteende som beror på den valda strategin.
Förklaring:
Strategy pattern handlar om att en klass (ofta kallad “context”) delegerar sitt beteende till en annan klass som representerar en specifik strategi. Denna strategi kan ändras vid körtid, vilket innebär att klassen som använder strategin (context) kan ändra sitt beteende dynamiskt beroende på vilken strategi som väljs. Detta mönster ökar flexibiliteten och underlättar underhåll genom att separera beteenden och göra dem utbytbara. De andra alternativen är inte korrekta eftersom de inte korrekt beskriver hur Strategy-mönstret fungerar.
Dancer dancer = new Dancer { Style = new Breakdance() }; interface IDanceStyle { string Dance(); } interface IDancer { string Dance(); } class Breakdance : IDanceStyle { } public string Dance() => "Breakdancing!"; class Dancer : IDancer { public IDanceStyle Style { get; set; } } public string Dance() => Style.Dance();
Ovan är ett exempel på Strategy pattern. Vilket gränssnitt används för strategier?
- IDancer
- Style
- IDanceStyle
Fråga 2:
Rätt svar: 3. IDanceStyle
Förklaring:
I Strategy pattern representerar gränssnittet (i det här fallet “IDanceStyle”) en familj av möjliga strategier eller beteenden. Konkreta strategier (som “Breakdance” i detta exempel) implementerar detta gränssnitt. “IDanceStyle” används för att definiera ett beteende som kan ändras dynamiskt av “Dancer” klassen vid körtid. “IDancer” är inte rätt svar eftersom det är ett gränssnitt som definierar beteendet för dansaren, inte strategierna de använder.
Vad blir output för följande kod?
Bird b1 = new Bird(new FlyWithWings()); Bird b2 = new Bird(new FlyWithRockets()); Console.WriteLine(b2.Fly()); string Fly(); class FlyWithWings : IFlyBehavior { interface IFlyBehavior { } } public string Fly() => "Flying with wings!"; class FlyWithRockets : IFlyBehavior { public string Fly() => "Flying with rockets!"; } class Bird { private IFlyBehavior behavior; public Bird(IFlyBehavior b) => behavior = b; public string Fly() => behavior.Fly(); }
Rätt svar: Output är: “Flying with rockets!”
Förklaring:
I exemplet skapas två “Bird” objekt med olika flygbeteenden: “FlyWithWings” och “FlyWithRockets”. När Console.WriteLine(b2.Fly()); körs, anropar den “Fly” metoden på “b2” objektet, vilket har beteendet från “FlyWithRockets” klassen. Metoden returnerar strängen “Flying with rockets!” vilket är det beteende som definierats i “FlyWithRockets” klassen.
```
Console.WriteLine(newCar(newDriveFast()).Drive());
public interfaceIDriveBehavior{ string Drive(); }
public class DriveFast : IDriveBehavior {
public string Drive() => “Driving fast!”;
}
public class Car {
private IDriveBehavior behavior;
public Car(IDriveBehavior b) => behavior = b;
public string Drive() => BLANK;
}
```
Output: Driving fast!
Vad ska ersätta BLANK ?
1. new DriveFast()
2. behavior.Drive()
3. this.Drive()
Rätt svar: 2. behavior.Drive()
Förklaring:
I koden ovan har “Car” klassen en medlem variabel “behavior” av typen “IDriveBehavior”, vilket representerar ett körbeteende. Rätt sätt att initiera en operation på denna medlem variabel är genom att anropa metoden som definieras i gränssnittet, vilket är “Drive()”. Därför ska “behavior.Drive()” användas för att anropa det faktiska körbeteendet hos det beteende som “Car” objektet håller. Alternativ 1 är felaktigt eftersom det skapar en ny instans av “DriveFast” vilket inte är vad som krävs här. Alternativ 3 är också felaktigt eftersom “this.Drive()” skulle referera till en metod inom samma klass, vilket inte är fallet här.
Vad är huvudsyftet med dependency injection?
- Att direkt koppla ett objekts beroenden.
2.Att invertera kontrollen av hur beroenden hanteras, där en klass mottar sina beroenden istället för att skapa dem. - Att öka komplexiteten i kodbasen för bättre säkerhetspraxis.
Alternativ 1: Att tillåta att objekt får sina beroenden utan att skapa dem själva.
Förklaring: Detta är korrekt. Dependency injection handlar om att ge ett objekt dess nödvändiga beroenden utan att det behöver skapa dem, vilket främjar löskoppling och enklare kodhantering.
Hur relaterar principen om “separation of concerns” till dependency injection?
- Den har ingen direkt relation till dependency injection.
- Den möjliggör dependency injection genom att blanda flera beroenden.
- Den stödjer dependency injection genom att uppmuntra en klass att endast hantera en uppgift, där beroenden injiceras snarare än skapas inuti.
Option 3: It supports dependency injection by encouraging a class to handle only one task, with dependencies being injected rather than created internally.
Explanation: This is correct. SoC recommends that each class or module in a program should address only one part of the functionality. This concept goes hand in hand with dependency injection, which maintains that objects should not create their dependencies but rather have them supplied (injected). This way, each class focuses solely on its primary purpose, promoting cleaner, more readable, and maintainable code.