- The completed implementation satisfies this requirement: Change the Name to "SOLIDPrinciples".
- The completed implementation satisfies this requirement: Change the Solution name to: 7.1 SOLID *YourLastName*.
7.1 SOLID Principles Lab
Work through the stories in order. Use the acceptance criteria to check each feature, and complete the nested tasks using the specified names, values, and scenarios.
Previously, the majority of projects you have worked on have been in .NET Framework. Here we are introducing you to another version of the C# language .NET Core, that has been made only fairly recently. Core is open source compared to Framework that is developed exclusively by Miscrosoft. Read the article below for more information.
.NET Core - Wikipedia
.NET Core - Wikipedia

This structure looks different from Framework in that this does not have a standalone exe file and has additional json files to help with configuration.
A file explorer window displaying the contents of the netcoreapp2.2 folder. In order top to bottom the following files are listedexplorer window displaying the contents of the netcoreapp2.2 folder. In order top to bottom the following files are listed:
:
SOLIDPrinciples.deps.json
SOLIDPrinciples.dll
SOLIDPrinciples.pdb
SOLIDPrinciples.runetimeconfig.dev.json
SOLIDPrinciples.runtimeconfig.json
This looks similar to the console app's debug folder without the json startup files because class libraries can't start up.
A File Explorer window displaying the contents of the netstandard2.0 folder. In order top to bottomExplorer window displaying the contents of the netstandard2.0 folder. In order top to bottom, the following files are displayedfollowing files are displayed:
:
OpenClosed.deps.json
OpenClosed.dll
OpenClosed.pdbSOLID Exercise.zip
SOLID Exercise.zipget.We have a Student class with some functionality that relates to an assignment. But what if the formula for grading changes? Should we have to open up the Student code that really doesn't have anything to do with assignments other than use them to change functionality? No.
It is easy to not want to create many classes for fear of cluttering up a solution/project, but it is always better than have many classes that do one thing than one large class that does many.
A UML Class diagram of the Assignment Class diagram of the Assignment class is displayed:
:
Fields:
- name: : string
- points- points: : int
Methods and Propertiesand Properties:
:
+ Assignment(name: string, points: int)
+ Name <get>
+ Points <get>
+TurnIn(motivation: int, skill: int)A UML Class diagram of the Student Class diagram of the Student class is displayed:
:
Fields:
- assignmentName: : string (red with a line striking it out)
- assignmentPoints: : int (red with a line striking it out)
-firstName: : string
-lastName-lastName: : string
-motivation-motivation: : int
-skill-skill: : int
Methods and Propertiesand Properties:
:
+ Student(firstName: string, lastName: string, motivation: int, skill: int)
+ DoAssignment(assignmentName: string, assignmentPoints: int, assignment: Assignment) (the existing parameters assignmentName and Assignment Points are red and with a line striking them out. The new parameter as
+ GoToClass()
+ TakeBreak()
+ TurnInAssignment() (red with a line striking it out)
We have now broken the Student class out so that it no longer has anything to do with how an assignment functions. Both the Student and Assignment class only have ONE responsibility now.
A console window displaying the following textwindow displaying the following text:
:
Line 11: : Doing assignment "Final Project" worth 60 points.
Line 2"Final Project" worth 60 points.
Line 2: : Received 54 out of 60.54 out of 60.get set.override.Classes should be OPEN to extension and CLOSED to modification. This means that a class should not need to be messed with if you need to add new features or derive a subclass from it.
What if we wanted to add more minions to the application? We would have to go in and change the Hero class to accommodate/modify that.
A UML Class diagram displaying the Minion <Class diagram displaying the Minion <abstract> class with 4 subclasses inheriting from itfrom it: : Goblin, Ogre, Zombie, and OwlBear.
Minion < Abstract >
Fields
# hp: int
# attack: int
# defense: int
Methods and PropertiesOwlBear.
Minion < Abstract >
Fields
# hp: int: int
# attack: int: int
# defense: int: int
Methods and Properties:
:
+ HP: : int <get> <set>
+ Attack: : int <get>
+ Defense: : int <get>
+ Battle(Hero hero) <virtual>
+ ToString() <override>
Goblin (inheriting Minion)
No Fields
Methods and Properties
+ Goblin(hpMethods and Properties
+ Goblin(hp: : int, attack: : int, defense: : int)
+ Battle(Hero)
+ Battle(Hero: : hero)
Ogre (inheriting Minion)
No Fields
Methods and Properties
+ Ogre(hp)
Ogre (inheriting Minion)
No Fields
Methods and Properties
+ Ogre(hp: : int, attack: : int, defense: : int)
+ Battle(Hero)
+ Battle(Hero: : hero)
Zombie(inheriting Minion)
No Fields
Methods and Properties
+ Zombie(hp)
Zombie(inheriting Minion)
No Fields
Methods and Properties
+ Zombie(hp: : int, attack: : int, defense: : int)
+ Battle(Hero)
+ Battle(Hero: : hero)
OwlBear(inheriting Minion)
No Fields
Methods and Properties
+ OwlBear(hp)
OwlBear(inheriting Minion)
No Fields
Methods and Properties
+ OwlBear(hp: : int, attack: : int, defense: : int)
+ Battle(Hero)
+ Battle(Hero: : hero))hero.HP -= (int)Math.Round(this.attack - hero.Defense * .20);
this.hp -= (int)Math.Round(hero.Attack - this.defense * .10);return $"{this.GetType().Name} HP: {this.hp}";Change the minion field from type string to type Minion.
Change the Battle method to take a Minion type not a string type.
Change the references to the minion in the hero methods to minion.GetType().Name
Ensure the your battle method contains the following:
Assign the minion field
Print out a new line
Print out the hero's ToString
Print out the minion's ToString
Call the minion's Battle Method, passing in the hero object
Print out the hero's ToString
Print out the minion's ToString
In the Main method, instantiate a Goblin, passing in 80, 40 and 10 as arguments and then call the hero's Battle method, passing in the Goblin object.

A console displaying the following textdisplaying the following text:
:
Line 11: : Open/Closed Principle
Line 22: : Blank
Line 33: : Hero HP: : 100
Line 44: : Goblin HP: : 80
Line 55: : Blank
Line 66: : Hero hacks n' slashes at the Goblin.
Line 7n' slashes at the Goblin.
Line 7: : Hero HP: : 68
Line 88: : Goblin HP: : 31
Line 99: : BlankNow that we modified the classes to that the Hero's Battle method changes depending on the object we no longer have to modify the Hero class if we need to add another minion. We could add a Dragon class and never have to touch the Hero class at all.
Along with our Hero we want a Sidekick. The Sidekick can do most of what the Hero can do with a little variation, but right now if we inherited from Hero the Sidekick would have to do everything exactly the same.
A class diagram showing the Sidekick Sidekick class inheriting from the Hero class.
.
Hero
All the Fieldsthe Fields, Methods and Properties of the existing Hero and Properties of the existing Hero class are displayed.
Sidekick inheriting from Hero (in blue text)
No Fields
Methods and Properties
+ Sidekick(hpfrom Hero (in blue text)
No Fields
Methods and Properties
+ Sidekick(hp: : int, attack: : int, defense: : int, magic: : int) (in blue text)
+ PutUpShield() <override> (in blue text)
+ Run() <override> (in blue text)) (in blue text)
+ PutUpShield() <override> (in blue text)
+ Run() <override> (in blue text)Instantiate a new Sidekick, passing in 80, 30, 20 and 10.
Call the sidekick's Battle method and pass in the Ogre object.

A console window displaying the following textwindow displaying the following text:
:
Line 11: : Open/Closed Principle
Line 22: : Blank
Line 33: : Hero HP: : 100
Line 44: : Ogre HP: : 80
Line 55: : Blank
Line 66: : Hero defends against the Ogre with their shield.
Line 7against the Ogre with their shield.
Line 7: : Hero HP: : 68
Line 88: : Ogre HP: : 31
Line 99: : Blank
Line 1010: : Sidekick HP: : 80
Line 1111: : Ogre HP: : 31
Line 1212: : Blank
Line 1313: : Sidekick throws body in front of Hero.
Line 14body in front of Hero.
Line 14: : Sidekick HP: : 44
Line 1515: : Ogre HP: : 2abstract.override.abstract.This principle states that subclasses should be able to be swapped without changing or breaking the application.
Console.WriteLine("\n---------------- Liskov Substitution Principle ----------------");
Birdkeeper birdkeeper = new Birdkeeper();
birdkeeper.AddBird(new Hawk());
birdkeeper.AddBird(new Duck());
birdkeeper.AddBird(new Pigeon());
birdkeeper.AddBird(new Penguin());
birdkeeper.FeedBirds();
birdkeeper.BirdsFlyAround();Start the application. An exception will be thrown because Penguins can't fly! But they're in a list of things that should be able to fly.
Since not all Birds can fly, we need to remove that behavior from the Bird class. This violates the Liskov Substitution Principle. You aren't able to treat Penguins and Hawks the same way because they don't function in similar ways in all their behaviors.
A UML class diagram is displayed. The new FlyingBird <abstractabstract> class is inheriting from the Bird <abstract> class. . The + Fly() <abstract> methods in in both Bird and Penguin are red and with a line striking them ou
FlyingBird <and Penguin are red and with a line striking them ou
FlyingBird <abstract> inheriting from Bird
No Fields
Methods and Properties
+ Fly() <Properties
+ Fly() <abstract>
Now the Penguin isn't forced to implement a method that they shouldn't and an exception won't be thrown. Additionally, shared behaviors are now properly inherited.
A console window displaying the following textwindow displaying the following text:
:
Line 11: : Liskov Substitution Principle
Line 2Principle
Line 2: : Hawk eats.
.
Line 33: : Duck eats.
.
Line 44: : Pigeon eats.
.
Line 55: : Penguin eats.
.
Line 66: : Hawk flies around.
Line 7around.
Line 7: : Duck flies around.
Line 8around.
Line 8: : Pigeon flies around.around.Clients should not have to be forced to implement members of an interface.
Console.WriteLine("\n---------------- Interface Segregation Principle ----------------");
Restaurant restaurant = new Restaurant(new Cook());
restaurant.OpenRestaurant();A UML Class Diagram displaying the Restaurant Class Diagram displaying the Restaurant class.
.
Restaurant
Fields
- cookFields
- cook: : Cook
- waiter: : Waiter (in blue text)
+ Restaurant(cook:Cook, waiter: Waiter) (The new parameter waiter: Waiter is in blue text)
+OpenRestaurant()We have a problem now. Waiters don't make food and Cooks don't take orders but they both have to implement methods because of the IWorker interface. The Restaurant still wants to make sure that only waiters take orders and only cooks make things.
this.cook.PunchIn();
this.waiter.PunchIn();
this.waiter.TakeOrder();
this.cook.MakeFood();
this.cook.PunchOut();
this.waiter.PunchOut();
Now both workers have the IWorker methods and aren't forced to implement something unrelated to their functionality.
A Console window displaying the following text
Line 1window displaying the following text
Line 1: : Interface Segregation Principle
Line 2Principle
Line 2: : Cook punching in.
Line 3in.
Line 3: : Waiter is punching in.
in.
Line 44: : Waiter is taking an order.
Line 5: : Cook making food.
Line 6food.
Line 6: : Cook punching out.
Line 7out.
Line 7: : Waiter is punching out.out.get set; EyeColor: string get set; HairColor: string get set; Weight: double get set.get set; EyeColor: string get set; HairColor: string get set; Weight: double get set.High-level classes should not depend on low-level classes. Instead, abstractions should be used.
Console.WriteLine("\n---------------- Dependency Inversion Principle ----------------");
CharacterBuilder characterBuilder = new CharacterBuilder();
characterBuilder.ChangeAge(33);
characterBuilder.ChangeEyeColor("Blue");
characterBuilder.ChangeHairColor("Brown");
characterBuilder.ChangeWeight(130);
Console.WriteLine(characterBuilder.ToString());In the CharacterBuilder class the only type we have is Human. But what about other types? Aliens? Monsters? Robots? This design only works if the character we are trying to build is a Human type.
A UML class diagram displaying the existing Human existing Human class and the new Monster class both implementing the new <<interface>> ICharacter.
<<interface>> ICharacter (in blue text)
No Fields
Methods and Properties
AgeMethods and Properties
Age: : int <get> <set> (in in blue text)
EyeColor)
EyeColor: : string <get> <set> (in in blue text)
HairColor)
HairColor: : string <get> <set> (in in blue text)
Weight)
Weight: : double <get> <set> (in in blue text)
Monster (implementing ICharacter) (in blue text)
No Fields
+ Age)
Monster (implementing ICharacter) (in blue text)
No Fields
+ Age: : int <get> <set> (in in blue text)
+ EyeColor)
+ EyeColor: : string <get> <set> (in in blue text)
+ HairColor)
+ HairColor: : string <get> <set> (in in blue text)
+ Weight)
+ Weight: : double <get> <set> (in in blue text)
Human (implementing ICharacter)
No Fields.
+ Age)
Human (implementing ICharacter)
No Fields.
+ Age: : int <get> <set> (in in blue text)
+ EyeColor)
+ EyeColor: : string <get> <set> (in in blue text)
+ HairColor)
+ HairColor: : string <get> <set> (in in blue text)
+ Weight)
+ Weight: : double <get> <set> (in in blue text))There is still an issue, however, because if you want to change the type of thing being instantiated you'd have to open the class. It is still dependent on a low-level class.
\\nType: {this.character.GetType().Name}\\nAfter the call to ToString, instantiate another CharacterBuilder and pass in a Monster type. Change the properties to whatever you like and call ToString again.
This is the power of Dependency Inversion. We can have two instances of the CharacterBuilder with two different types being passed it. All the class cares about is that the "thing" is an ICharacter. It is no longer dependent on knowing what the object type really is.

A Console window displaying the following textwindow displaying the following text:
:
Line 11: : Dependency Inversion Principle
Line 2Principle
Line 2: : Blank
Line 33: : Type: Human
Line 4: : Age: 33
Line 55: : Eye Color: : Blue
Line 66: : Hair Color: : Brown
Line 77: : Weight: 130
Line 88: : Blank
Line 99: : Type: Monster
Line 10: : Age: 350
Line 1111: : Eye Color: : Red
Line 1212: : Hair color: : Black
Line 1313: : Weight: 400Return to each story and verify its acceptance criteria. Preserve the submission format and destination stated in the activity; a reading, setup guide, lab, video demonstration, and oral final may require different evidence.
Create a new project. Create a Class Library Import classes and projects. Single Responsibility Principle Open/Closed Principle Liskov Substitution Principle Interface Segregation Principle Dependency Inversion Principle