diff --git a/ShoppingExample/ConsoleApp/ConsoleApp.csproj b/ShoppingExample/ConsoleApp/ConsoleApp.csproj
index 2150e37..87ef1c5 100644
--- a/ShoppingExample/ConsoleApp/ConsoleApp.csproj
+++ b/ShoppingExample/ConsoleApp/ConsoleApp.csproj
@@ -7,4 +7,8 @@
enable
+
+
+
+
diff --git a/ShoppingExample/ConsoleApp/Program.cs b/ShoppingExample/ConsoleApp/Program.cs
index a6559fa..6b97127 100644
--- a/ShoppingExample/ConsoleApp/Program.cs
+++ b/ShoppingExample/ConsoleApp/Program.cs
@@ -1,4 +1,8 @@
using System;
+using System.Reflection.Metadata.Ecma335;
+using ProductsLib;
+using ProductsLib.Clothing;
+using ProductsLib.Electronics;
class Program
{
@@ -7,7 +11,10 @@ static void Main(string[] args)
var mansShirt = new MansShirt(
"FormalShirt",
"Smart white shirt for business meetings",
- Price.Pounds(25.0)
+ Price.Pounds(25.0),
+ "Leather",
+ "Winter",
+ true
);
var laptop = new Laptop(
@@ -16,6 +23,8 @@ static void Main(string[] args)
Price.Euros(999.99)
);
+
+
// Homework: create more products of different types, priced in different currencies,
// and some power tools that work in different countries
@@ -53,203 +62,3 @@ private static void DisplayTheMostExpensiveProduct(Product[] products)
}
}
-
-// Product is something we can buy/sell
-// it's abstract. Why?
-// Product has a Price, a Name, a Description
-//
-public abstract class Product
-{
- // Homework: Name, Description, Price are properties. Find out about this, and learn how
- // a property combines a field and a method. Also, find out what "init" means here.
- public string Name { get; init; }
- public string Description { get; init; }
- public Price Price { get; init; }
- public Product(string name, string description, Price price)
- {
- Name = name;
- Description = description;
- Price = price;
- }
-}
-
-
-// We want to sell our products to many countries, so
-// Price needs both a number and a currency.
-// We might be using Price a lot in many functions and these functions
-// might be called many times. It's a small object though (how many bytes?)
-// so what is the best way to represent it? a stack object or a heap object?
-public struct Price
-{
- public double Amount { get; init; }
- public string Currency { get; init; }
- private Price(double amount, string currency)
- {
- Amount = amount;
- Currency = currency;
- }
- // Homework: how does the factory method pattern help you to make
- // sure that Price objects are always created correctly with currency names that are valid?
- public static Price Pounds(double amount)
- {
- // Homework, throw exception if amount is negative
- return new Price(amount, "GBP");
- }
-
- public static Price Euros(double amount)
- {
- // Homework, throw exception if amount is negative
- return new Price(amount, "EUR");
- }
-
- // Homework: add more factory methods for other currencies like USD, CAD, AUD, JPY, etc.
-
-}
-
-// The first layer of derived classes are high level categories like
-// Electronics, Clothing, Food, Tools, Motor Vehicles even Services and Vouchers
-// These are still abstract classes because we can't create one without knowing
-// exactly what it is
-public abstract class Electronics : Product
-{
- public Electronics(string name, string description, Price price)
- : base(name, description, price)
- {
- }
-}
-public abstract class Clothing : Product
-{
- public Clothing(string name, string description, Price price)
- : base(name, description, price)
- {
- }
-}
-public abstract class Food : Product, IHasExpiryDate
-{
- // Homework: what does "DateTime? expiryDate = null" mean?
- public Food(string name, string description, Price price, DateTime? expiryDate = null)
- : base(name, description, price)
- {
- // Homework: what does this line do? What does "??" mean?
- ExpiryDate = expiryDate ?? DateTime.MaxValue;
- }
-
- // Homework: notice that the ExpiryDate property was declared on the
- // interface and has to be implemented here (just like abstract methods)
- public DateTime ExpiryDate { get; set; }
-}
-public abstract class Tools : Product
-{
- public Tools(string name, string description, Price price)
- : base(name, description, price)
- {
- }
-}
-public abstract class MotorVehicles : Product
-{
- public MotorVehicles(string name, string description, Price price)
- : base(name, description, price)
- {
- }
-}
-public abstract class Services : Product, IPhysicalOrDigital
-{
- public Services(string name, string description, Price price, bool isDigital = false)
- : base(name, description, price)
- {
- IsDigital = isDigital;
- }
-
- public bool IsDigital { get; init ; }
-}
-public abstract class Vouchers : Product, IHasExpiryDate, IPhysicalOrDigital
-{
- public Vouchers(string name, string description, Price price, DateTime? expiryDate, bool isDigital = true)
- : base(name, description, price)
- {
- IsDigital = isDigital;
- }
-
- // Homework: implement this property from the interface
- public DateTime ExpiryDate {
- get => throw new NotImplementedException();
- set => throw new NotImplementedException();
- }
- public bool IsDigital { get; init; }
-}
-
-//
-// In clothing, we have Mens and Womens (still abstract!)
-//
-public abstract class MensClothing : Clothing
-{
- public MensClothing(string name, string description, Price price)
- : base(name, description, price)
- {
- }
-}
-public abstract class WomensClothing : Clothing
-{
- public WomensClothing(string name, string description, Price price)
- : base(name, description, price)
- {
- }
-}
-
-// Finally, we have concrete classes that we can instantiate
-// For example, in MensClothing, we have Shirts and MansTrousers
-// and in Electronics we have Phones and Laptops
-public class MansShirt : MensClothing
-{
- public MansShirt(string name, string description, Price price)
- : base(name, description, price)
- {
- }
-}
-public class MansTrousers : MensClothing
-{
- public MansTrousers(string name, string description, Price price)
- : base(name, description, price)
- {
- }
-}
-public class Phone : Electronics
-{
- public Phone(string name, string description, Price price)
- : base(name, description, price)
- {
- }
-}
-public class Laptop : Electronics
-{
- public Laptop(string name, string description, Price price)
- : base(name, description, price)
- {
- }
-}
-
-// Homework
-// 1. Add more concrete product classes like WomensClothing, Food items, Motor Vehicles
-// 2. Tools can be power tools or hand tools. Create classes for these. Abstract or not?
-// 3. Power tools only work in certain countries because of voltage differences and plug shapes.
-// How would you represent this in your class design? You need to tell the customer whether
-// the power tool will work in "UK", "USA/CANADA", "EUROPE", etc.
-// 4. Look at the interface I have specified below for items that have an expiry date, like Food or Vouchers.
-// Some Foods need to be eaten before expiry date, and Vouchers sometimes need to be used before expiry date.
-
-public interface IHasExpiryDate
-{
- DateTime ExpiryDate { get; set; }
-}
-
-// Vouchers and Services can be provided physically or digitally, so they implement this interface
-public interface IPhysicalOrDigital
-{
- bool IsDigital { get; init; }
-}
-
-// 5. Look at the Vouchers class. Notice it inherits from Product but also implements more than
-// one interface. This is the big difference between classes and interfaces in C#. If using only
-// classes, we would have to use single inheritance, which means the structure can only be a tree.
-// But with interfaces, we can implement multiple interfaces, so the structure can be a graph.
-// Real life products are more like a graph than a tree!
\ No newline at end of file
diff --git a/ShoppingExample/ExtensionMethodTests/ExtensionMethodTests.csproj b/ShoppingExample/ExtensionMethodTests/ExtensionMethodTests.csproj
new file mode 100644
index 0000000..c28909f
--- /dev/null
+++ b/ShoppingExample/ExtensionMethodTests/ExtensionMethodTests.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ShoppingExample/ExtensionMethodTests/UnitTest1.cs b/ShoppingExample/ExtensionMethodTests/UnitTest1.cs
new file mode 100644
index 0000000..f6dd984
--- /dev/null
+++ b/ShoppingExample/ExtensionMethodTests/UnitTest1.cs
@@ -0,0 +1,286 @@
+using ProductsLib;
+using ProductsLib.Clothing;
+using ProductsLib.Food;
+using System.Xml.Schema;
+
+namespace ExtensionMethodTests
+{
+
+
+ // HomeWork
+ public class MealTestList
+ {
+ public List Meals { get; } = new List
+ {
+ new ReadyMeal( "pasta",
+ "pasta",
+ Price.Pounds(1),
+ false,
+ DietType.Vegan,
+ DateTime.Now.AddDays(5)
+
+ ),
+ new ReadyMeal(
+ "Chicken Pie",
+ "Chicken Pie",
+ Price.Pounds(5),
+ false,
+ DietType.ContainMeats,
+ DateTime.Now.AddDays(3)
+ ),
+ new ReadyMeal(
+
+ "Fish Finger",
+ "Fish Finger",
+ Price.Pounds(3),
+ true,
+ DietType.ContainMeats,
+ DateTime.Now.AddDays(7)
+
+
+ ),
+ new ReadyMeal(
+
+ "Bread",
+ "Bread",
+ Price.Pounds(2),
+ true,
+ DietType.Vegeterain,
+ DateTime.Now.AddDays(7)
+ ),
+
+ new ReadyMeal(
+
+ "cake",
+ "cake",
+ Price.Pounds(3),
+ false,
+ DietType.Vegeterain,
+ DateTime.Now.AddDays(7)
+ )
+
+
+
+ };
+
+
+
+
+ }
+
+ //
+ // Example of a class that contains Extension Methods
+ public static class ExtensionMethods
+ {
+
+ // Example extension method
+ public static string MakeUpperCaseAndReverse(this string input)
+ {
+ return string.Join("", input.ToUpper().Reverse());
+ }
+ // Second extension method
+ public static int AddOne(this int input)
+ {
+ return input + 1;
+ }
+ // Generic extension method
+ public static string UpperCaseStringRepresentation(this T input)
+ {
+ return input.ToString().ToUpper();
+ }
+
+
+ public static bool IsExpired(this Food food)
+ {
+ return food.ExpiryDate < DateTime.Now;
+ }
+
+
+ public static IEnumerable CheapMeals(this IEnumerable foods,double maxPrice)
+ {
+ return foods.Where(f => f.Price.Amount <= maxPrice);
+ }
+
+ }
+
+
+
+
+ public class ExtensionMethodTests
+ {
+ [SetUp]
+ public void Setup()
+ {
+ }
+
+
+
+ [Test]
+ public void TestLinqIEnumerable()
+ {
+ var meals = new MealTestList();
+
+ //var TestAggregatw = meals.Meals.Aggregate(());
+
+ bool TestAll = meals.Meals.All(m => m.IsGlutenFree);
+
+ bool TestAny = meals.Meals.Any(m => m.DietType == DietType.ContainMeats);
+
+ var TestGroupBy = meals.Meals.GroupBy(m => m.DietType);
+
+
+
+ var Testindexed = meals.Meals.Select((m, i) => new { Index = i, m.Name });
+
+
+ var repeated = Enumerable.Repeat("pasta", 3);
+
+ var TestAppend = meals.Meals.Append(
+ new ReadyMeal
+ ("tuna",
+ "tuna",
+ Price.Pounds(8),
+ true,
+ DietType.ContainMeats,
+ DateTime.Now.AddDays(3)
+ )
+ ).ToList();
+
+
+
+ // AsEnumerable()
+ // cast
+
+ var TestChunk = meals.Meals.Chunk(3);
+
+ foreach (var chunk in TestChunk)
+ {
+ Console.WriteLine("chunk");
+ foreach (var meal in chunk)
+ {
+ Console.WriteLine($" {meal.Name}");
+ }
+
+
+ var TestAvrage = meals.Meals.Average(m => m.Price.Amount);
+ var TestMin = meals.Meals.Min(m => m.Price.Amount);
+ var TestMax = meals.Meals.Max(m => m.Name.Length);
+ var TestMinBy = meals.Meals.MinBy(m => m.Name.Length);
+ //bool TestContain = meals.Meals.Contains(pasta);
+
+ //var reversedMeals = meals.Meals.Reverse().ToList();
+
+
+ var TestSelect = meals.Meals.Select(m => m.Name).ToList();
+
+ var TestWhere = meals.Meals.Where(m => m.DietType == DietType.Vegan).ToList();
+
+ // Food extension
+ bool expired = meals.Meals.First().IsExpired();
+
+ // Collection extension
+ var cheapMeals = meals.Meals.CheapMeals(3).ToList();
+
+ }
+ }
+
+
+
+ [Test]
+ public void TestLinqIEnumerableTest()
+ {
+ var meals = new MealTestList();
+
+
+ bool allGlutenFree = meals.Meals.All(m => m.IsGlutenFree);
+ Assert.IsFalse(allGlutenFree);
+
+
+ bool anyContainMeat = meals.Meals.Any(m => m.DietType == DietType.ContainMeats);
+ Assert.IsTrue(anyContainMeat);
+
+
+ var grouped = meals.Meals.GroupBy(m => m.DietType);
+ Assert.AreEqual(3, grouped.Count());
+
+
+ var mealNames = meals.Meals.Select(m => m.Name).ToList();
+ CollectionAssert.Contains(mealNames, "pasta");
+ CollectionAssert.Contains(mealNames, "cake");
+
+
+ var veganMeals = meals.Meals.Where(m => m.DietType == DietType.Vegan).ToList();
+ Assert.IsTrue(veganMeals.All(m => m.DietType == DietType.Vegan));
+ Assert.AreEqual(2, veganMeals.Count);
+
+
+ bool firstMealExpired = meals.Meals.First().IsExpired();
+ Assert.IsFalse(firstMealExpired);
+
+
+ var cheapMeals = meals.Meals.CheapMeals(3).ToList();
+ Assert.IsTrue(cheapMeals.All(m => m.Price.Amount <= 3));
+ Assert.AreEqual(4, cheapMeals.Count);
+
+
+
+
+ var updatedMeals = meals.Meals.Append(
+ new ReadyMeal("tuna", "tuna", Price.Pounds(8), true, DietType.ContainMeats, DateTime.Now.AddDays(3))
+ ).ToList();
+ Assert.AreEqual(meals.Meals.Count + 1, updatedMeals.Count); // Count increased by 1
+ Assert.AreEqual("tuna", updatedMeals.Last().Name); // Last meal is tuna
+
+
+
+ var chunks = meals.Meals.Chunk(3).ToList();
+ Assert.AreEqual(2, chunks.Count); // With 5 meals, chunk size 3 ? 2 chunks
+ Assert.AreEqual(3, chunks[0].Length); // First chunk has 3 meals
+ Assert.AreEqual(2, chunks[1].Length); // Second chunk has 2 meals
+ }
+
+
+
+
+
+
+ [Test]
+ public void Test1()
+ {
+ string myString = "abcdef";
+
+ var output = myString.MakeUpperCaseAndReverse();
+ var netVersion = myString.ToUpper();
+
+ double x = 9;
+ var tx = x.UpperCaseStringRepresentation();
+
+ DateTime dt = DateTime.Now;
+ var dtt = dt.UpperCaseStringRepresentation();
+
+ Assert.Pass();
+ }
+
+ [Test]
+ public void TestClothingBuilder()
+ {
+ var shirt = new MansShirt("Shirt", "Woolly Shirt", Price.Pounds(20.0));
+ var woollyShirt = shirt.MakeWool();
+
+ }
+
+
+
+ }
+
+ // This is not the way the classic "Builder" pattern is implemented, but just
+ // an example of using extension methods to implement a set of "Builder" methods
+ public static class ClothingBuilder
+ {
+ public static Clothing MakeWool(this Clothing item)
+ {
+ item.Material = "Wool";
+ return item;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ShoppingExample/ProductUnitTests/Collections/TestCollections.cs b/ShoppingExample/ProductUnitTests/Collections/TestCollections.cs
new file mode 100644
index 0000000..b273bf4
--- /dev/null
+++ b/ShoppingExample/ProductUnitTests/Collections/TestCollections.cs
@@ -0,0 +1,145 @@
+using NUnit.Framework;
+using ProductsLib;
+using ProductsLib.Electronics;
+using ProductsLib.Food;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ProductUnitTests.Collections
+{
+ [TestFixture]
+ internal class TestCollections
+ {
+ [Test]
+ public void List()
+ {
+ var list = new List();
+
+ list.Add(1);
+ list.Add(2);
+ for (int i =3; i<10000; i++)
+ {
+ list.Add(i);
+ }
+ var count = list.Count;
+
+ //
+ // num => num > 5000
+ // this is an anonymous function. It receives a parameter called num and return num > 5000 (which is a bool)
+ var bigNumbers = list
+ .Where(num => num > 5000) // Where is a Linq function. All of the Linq functions belong to IEnumerable
+ .ToList();
+
+ var totalOfBigNumbers = bigNumbers.Sum(); // Sum is an aggregate function (outputs only one value)
+
+ var howManyEvenNumbers = list
+ .Where(num => num % 2 == 0)
+ .Count();
+ }
+
+ public class Box
+ {
+ public T Value { get; }
+
+ public Box(T value)
+ {
+ Value = value;
+ }
+ }
+
+ [Test]
+ public void Generic_Box_ShouldStoreAnyType()
+ {
+
+ var intBox = new Box(10);
+ Assert.AreEqual(10, intBox.Value);
+
+
+ var stringBox = new Box("Hello");
+ Assert.AreEqual("Hello", stringBox.Value);
+
+
+ var laptop = new Laptop("Dell", "Gaming laptop", Price.Pounds(1500));
+ var electronicsBox = new Box(laptop);
+
+ Assert.AreEqual("Dell", electronicsBox.Value.Name);
+ }
+
+
+
+
+ [Test]
+ public void TestElectricalsList()
+ {
+ var list = new List();
+
+ for (var i = 0; i < 10000; i++)
+ {
+ var laptop = new Laptop($"Laptop_{i}", "A laptop", Price.Pounds(1000 + i / 100));
+ var phone = new Phone($"Phone_{i}", "A phone", Price.Pounds(300 + i / 100));
+ list.Add(laptop);
+ list.Add(phone);
+ }
+
+ //var countPhones = list.Where(e => e is Phone).Count();
+ var countPhones = list.OfType().Count();
+
+ var eleventhItem = list[10];
+
+ var productNameList = list
+ .Select(e => e.Name)
+ .Select(f => f.Length)
+ .ToList();
+
+ //var productNameLengthList = list
+ // .Select(e => e.Name + " " + e.Name.Length.ToString())
+ // .ToList();
+
+ var productNameLengthList = list
+ .Select(e => new
+ {
+ e.Name,
+ e.Name.Length
+ })
+ .ToList();
+ }
+
+ [Test]
+ public void TestDictionaruElectricals()
+ {
+ var dictionary = new Dictionary();
+
+ for (var i = 0; i < 10000; i++)
+ {
+ var laptopKey = $"Laptop_{i}";
+ var laptop = new Laptop(laptopKey, "A laptop", Price.Pounds(1000 + i / 100));
+ var phoneKey = $"Phone_{i}";
+ var phone = new Phone(phoneKey, "A phone", Price.Pounds(300 + i / 100));
+ dictionary[laptopKey] = laptop;
+ dictionary[phoneKey] = phone;
+ }
+
+
+ var laptop_789 = dictionary["Laptop_789"];
+
+
+ }
+ [Test]
+
+ public void TestFood()
+ {
+
+ var foods = new ReadyMeal("Spagetti", "pasta", Price.Pounds(12) , false , DietType.Vegan);
+
+ var list = new List();
+
+ for (var i = 0; i <=10; i++)
+ {
+ list.Add(i);
+ }
+ }
+ }
+}
diff --git a/ShoppingExample/ProductUnitTests/ProductUnitTests.csproj b/ShoppingExample/ProductUnitTests/ProductUnitTests.csproj
new file mode 100644
index 0000000..c28909f
--- /dev/null
+++ b/ShoppingExample/ProductUnitTests/ProductUnitTests.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ShoppingExample/ProductUnitTests/TestClothing.cs b/ShoppingExample/ProductUnitTests/TestClothing.cs
new file mode 100644
index 0000000..d935987
--- /dev/null
+++ b/ShoppingExample/ProductUnitTests/TestClothing.cs
@@ -0,0 +1,30 @@
+using ProductsLib;
+using ProductsLib.Voucher;
+using ProductsLib.Clothing;
+
+namespace ProductUnitTests;
+
+public class TestClothing
+{
+
+ private Clothing cloth;
+ [SetUp]
+
+ public void Setup()
+ {
+ cloth = new WomenClothing("Pijamas", "long sleeves", Price.Pounds(12.5), "Leather" , "Winter", true);
+ }
+
+ [Test]
+ public void ClothingName()
+ {
+ Assert.That(cloth.Name, Is.EqualTo("Pijamas"));
+
+ }
+ [Test]
+ public void ClothingPrice()
+ {
+ Assert.That(cloth.Price.Amount, Is.EqualTo(12.5).Within(0.01));
+ Assert.That(cloth.Price.Currency,Is.EqualTo("GBP"));
+ }
+}
diff --git a/ShoppingExample/ProductUnitTests/TestFood.cs b/ShoppingExample/ProductUnitTests/TestFood.cs
new file mode 100644
index 0000000..d32a76b
--- /dev/null
+++ b/ShoppingExample/ProductUnitTests/TestFood.cs
@@ -0,0 +1,96 @@
+using ProductsLib;
+using ProductsLib.Food;
+using ProductsLib.Interfaces;
+
+namespace ProductUnitTests;
+
+public class TestFood
+{
+ private Food food;
+ private Food notExpiredFood;
+ private Food expiredFood;
+ private Food bread;
+
+
+
+ [SetUp]
+ public void Setup()
+ {
+ notExpiredFood = new ReadyMeal(
+ "Meat",
+ "Steak",
+ Price.Pounds(6),
+ true,
+ DietType.ContainMeats,
+ DateTime.Now.AddDays(5)
+
+ );
+
+
+
+ expiredFood = new ReadyMeal(
+ "Fish",
+ "Salmon",
+ Price.Pounds(7),
+ true,
+ DietType.ContainMeats,
+ DateTime.Now.AddDays(-1)
+
+ );
+ food = new ReadyMeal(
+ "Noddels",
+ "noddels",
+ Price.Pounds(1),
+ false,
+ DietType.Vegeterain
+
+ );
+
+ bread = new ReadyMeal(
+ "Bread",
+ "bread",
+ Price.Pounds(1),
+ false,
+ DietType.Vegan
+ );
+
+ }
+
+
+ public void NotExpiredFood_ShouldNotBeExpired()
+ {
+ Assert.IsFalse(notExpiredFood.IsExpired);
+ Assert.That(notExpiredFood.ExpiryDate, Is.GreaterThan(DateTime.Now));
+ }
+
+ [Test]
+ public void ExpiredFood_ShouldBeExpired()
+ {
+ Assert.IsTrue(expiredFood.IsExpired);
+ Assert.That(expiredFood.ExpiryDate, Is.LessThan(DateTime.Now));
+ }
+
+ [Test]
+ public void PriceChecked()
+ {
+ Assert.That(notExpiredFood.Price.Amount, Is.EqualTo(6));
+ Assert.That(notExpiredFood.Price.Currency, Is.EqualTo("GBP"));
+ }
+
+ [Test]
+
+ public void IsVegeterian()
+ {
+
+ Assert.That(food.DietType, Is.EqualTo(DietType.Vegeterain));
+
+ }
+ [Test]
+ public void IsNotGlutenFree()
+ {
+
+ Assert.IsFalse(food.IsGlutenFree);
+ }
+
+}
+
diff --git a/ShoppingExample/ProductUnitTests/TestTool.cs b/ShoppingExample/ProductUnitTests/TestTool.cs
new file mode 100644
index 0000000..328c2d9
--- /dev/null
+++ b/ShoppingExample/ProductUnitTests/TestTool.cs
@@ -0,0 +1,37 @@
+using NUnit.Framework;
+using ProductsLib;
+using ProductsLib.Tool;
+
+namespace ProductUnitTests
+{
+ public class TestTool
+ {
+ private Tools tool;
+
+ [SetUp]
+ public void Setup()
+ {
+ tool = new Hammer("Hammer", "IKA", Price.Pounds(12.5));
+ }
+
+ [Test]
+ public void Hammer_ShouldBeHandTool()
+ {
+ Assert.IsTrue(tool.IsHandTool);
+ }
+
+ [Test]
+ public void Hammer_HasCorrectPrice()
+ {
+ Assert.That(tool.Price.Amount, Is.EqualTo(12.5));
+ Assert.That(tool.Price.Currency, Is.EqualTo("GBP"));
+ }
+
+ [Test]
+ public void Hammer_HasCorrectNameAndDescription()
+ {
+ Assert.That(tool.Name, Is.EqualTo("Hammer"));
+ Assert.That(tool.Description, Is.EqualTo("IKA"));
+ }
+ }
+}
diff --git a/ShoppingExample/ProductUnitTests/TestVouchers.cs b/ShoppingExample/ProductUnitTests/TestVouchers.cs
new file mode 100644
index 0000000..12489f0
--- /dev/null
+++ b/ShoppingExample/ProductUnitTests/TestVouchers.cs
@@ -0,0 +1,37 @@
+using ProductsLib;
+using ProductsLib.Voucher;
+using ProductsLib.Clothing;
+
+namespace ProductUnitTests
+{
+ public class TestVouchers
+ {
+ private TeaBagVoucher voucher;
+
+ [SetUp]
+ public void Setup()
+ {
+ voucher = new TeaBagVoucher("Teabags", "Half Price Tea", Price.Pounds(1.50), new DateTime(2025, 12, 31), false);
+ }
+
+ [Test]
+ public void TestNewTeabagVoucher()
+ {
+
+ Assert.That(voucher, Is.Not.Null);
+ Assert.That(voucher.Name, Is.EqualTo("Teabags"));
+
+ }
+
+ [Test]
+ public void TestNewTeabagVoucherExpiryDate()
+ {
+
+ Assert.That(voucher, Is.Not.Null);
+ Assert.That(voucher.ExpiryDate, Is.EqualTo(new DateTime(2025,12,31)));
+ Assert.That(voucher.IsExpired, Is.False); // Not a good test because it will fail after some time.
+
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/ShoppingExample/ProductsLib/Clothing/Clothing.cs b/ShoppingExample/ProductsLib/Clothing/Clothing.cs
new file mode 100644
index 0000000..e02c05e
--- /dev/null
+++ b/ShoppingExample/ProductsLib/Clothing/Clothing.cs
@@ -0,0 +1,26 @@
+
+namespace ProductsLib.Clothing
+{
+ public abstract class Clothing : Product
+ {
+ public string Material { get; set; }
+ public string Season { get; set; }
+ public bool HandWashOnly { get; set; }
+
+ //public Clothing(string name, string description, Price price)
+ // : base(name, description, price)
+ //{ }
+
+ public Clothing(string name, string description, Price price , string material , string season , bool handwashonly)
+ : base(name, description, price)
+ {
+
+ Material = material;
+ Season = season;
+ handwashonly = true;
+
+ }
+ }
+
+
+}
diff --git a/ShoppingExample/ProductsLib/Clothing/MansShirt.cs b/ShoppingExample/ProductsLib/Clothing/MansShirt.cs
new file mode 100644
index 0000000..737e16a
--- /dev/null
+++ b/ShoppingExample/ProductsLib/Clothing/MansShirt.cs
@@ -0,0 +1,16 @@
+namespace ProductsLib.Clothing
+{
+ public class MansShirt : MensClothing
+ {
+ public MansShirt(string name, string description, Price price)
+ : base(name, description, price, "", "", false)
+ {
+ }
+ public MansShirt(string name, string description, Price price , string material , string season , bool handwashonly)
+ : base(name, description, price , material, season , handwashonly)
+ {
+ }
+ }
+
+
+}
diff --git a/ShoppingExample/ProductsLib/Clothing/MansTrousers.cs b/ShoppingExample/ProductsLib/Clothing/MansTrousers.cs
new file mode 100644
index 0000000..344ada6
--- /dev/null
+++ b/ShoppingExample/ProductsLib/Clothing/MansTrousers.cs
@@ -0,0 +1,12 @@
+namespace ProductsLib.Clothing
+{
+ public class MansTrousers : MensClothing
+ {
+ public MansTrousers(string name, string description, Price price , string material , string season , bool handwashonly)
+ : base(name, description, price , material , season, handwashonly )
+ {
+ }
+ }
+
+
+}
diff --git a/ShoppingExample/ProductsLib/Clothing/MensClothing.cs b/ShoppingExample/ProductsLib/Clothing/MensClothing.cs
new file mode 100644
index 0000000..7ddafd2
--- /dev/null
+++ b/ShoppingExample/ProductsLib/Clothing/MensClothing.cs
@@ -0,0 +1,12 @@
+namespace ProductsLib.Clothing
+{
+ public abstract class MensClothing : Clothing
+ {
+ public MensClothing(string name, string description, Price price , string material , string season , bool handwashonly)
+ : base(name, description, price ,material , season , handwashonly)
+ {
+ }
+ }
+
+
+}
diff --git a/ShoppingExample/ProductsLib/Clothing/WomenClothing.cs b/ShoppingExample/ProductsLib/Clothing/WomenClothing.cs
new file mode 100644
index 0000000..cfe483b
--- /dev/null
+++ b/ShoppingExample/ProductsLib/Clothing/WomenClothing.cs
@@ -0,0 +1,14 @@
+namespace ProductsLib.Clothing
+{
+ public class WomenClothing : Clothing
+ {
+ public WomenClothing(string name, string description, Price price , string material , string season , bool handwashonly)
+ :base(name, description, price , material , season , handwashonly)
+
+ {
+
+ }
+ }
+
+
+}
diff --git a/ShoppingExample/ProductsLib/Clothing/WomensClothing.cs b/ShoppingExample/ProductsLib/Clothing/WomensClothing.cs
new file mode 100644
index 0000000..45e46da
--- /dev/null
+++ b/ShoppingExample/ProductsLib/Clothing/WomensClothing.cs
@@ -0,0 +1,12 @@
+namespace ProductsLib.Clothing
+{
+ public abstract class WomensClothing : Clothing
+ {
+ public WomensClothing(string name, string description, Price price , string material , string season , bool handwashonly)
+ : base(name, description, price , material , season, handwashonly)
+ {
+ }
+ }
+
+
+}
diff --git a/ShoppingExample/ProductsLib/Electronics/Electronics.cs b/ShoppingExample/ProductsLib/Electronics/Electronics.cs
new file mode 100644
index 0000000..73aafbc
--- /dev/null
+++ b/ShoppingExample/ProductsLib/Electronics/Electronics.cs
@@ -0,0 +1,13 @@
+namespace ProductsLib.Electronics
+{
+ public abstract class Electronics : Product
+ {
+
+ public Electronics(string name, string description, Price price)
+ : base(name, description, price)
+ {
+ }
+ }
+
+
+}
diff --git a/ShoppingExample/ProductsLib/Electronics/Laptop.cs b/ShoppingExample/ProductsLib/Electronics/Laptop.cs
new file mode 100644
index 0000000..9b797bf
--- /dev/null
+++ b/ShoppingExample/ProductsLib/Electronics/Laptop.cs
@@ -0,0 +1,12 @@
+namespace ProductsLib.Electronics
+{
+ public class Laptop : Electronics
+ {
+ public Laptop(string name, string description, Price price)
+ : base(name, description, price)
+ {
+ }
+ }
+
+
+}
diff --git a/ShoppingExample/ProductsLib/Electronics/Phone.cs b/ShoppingExample/ProductsLib/Electronics/Phone.cs
new file mode 100644
index 0000000..e342bfa
--- /dev/null
+++ b/ShoppingExample/ProductsLib/Electronics/Phone.cs
@@ -0,0 +1,12 @@
+namespace ProductsLib.Electronics
+{
+ public class Phone : Electronics
+ {
+ public Phone(string name, string description, Price price)
+ : base(name, description, price)
+ {
+ }
+ }
+
+
+}
diff --git a/ShoppingExample/ProductsLib/Food/DietType.cs b/ShoppingExample/ProductsLib/Food/DietType.cs
new file mode 100644
index 0000000..a325065
--- /dev/null
+++ b/ShoppingExample/ProductsLib/Food/DietType.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ProductsLib.Food
+{
+ public enum DietType
+ {
+ Vegan,
+ Vegeterain,
+ ContainMeats
+
+ }
+}
diff --git a/ShoppingExample/ProductsLib/Food/Food.cs b/ShoppingExample/ProductsLib/Food/Food.cs
new file mode 100644
index 0000000..6006b5b
--- /dev/null
+++ b/ShoppingExample/ProductsLib/Food/Food.cs
@@ -0,0 +1,30 @@
+using ProductsLib.Interfaces;
+
+
+namespace ProductsLib.Food
+{
+ public abstract class Food : Product, IHasExpiryDate
+ {
+
+ public bool IsGlutenFree { get; set; }
+ public DateTime ExpiryDate { get; set; }
+ public DietType DietType { get; set; }
+
+ public bool IsExpired => DateTime.Now >= ExpiryDate;
+ public Food(string name, string description, Price price , bool isGluteemFree,DietType dietType, DateTime? expiryDate = null)
+ : base(name, description, price)
+ {
+
+
+ IsGlutenFree = isGluteemFree;
+ DietType = dietType;
+ ExpiryDate = expiryDate ?? DateTime.MaxValue;
+
+ }
+
+
+
+ }
+
+
+}
diff --git a/ShoppingExample/ProductsLib/Food/ReadyMeal.cs b/ShoppingExample/ProductsLib/Food/ReadyMeal.cs
new file mode 100644
index 0000000..ea246bd
--- /dev/null
+++ b/ShoppingExample/ProductsLib/Food/ReadyMeal.cs
@@ -0,0 +1,14 @@
+using System.Data.SqlTypes;
+
+namespace ProductsLib.Food
+{
+ public class ReadyMeal : Food
+ {
+ public ReadyMeal(string name, string description, Price price , bool isGlutenfree , DietType dietType, DateTime? expiryDate = null) : base(name, description, price, isGlutenfree, dietType, expiryDate)
+ {
+
+ }
+ }
+
+
+}
diff --git a/ShoppingExample/ProductsLib/Interfaces/IHasExpiryDate.cs b/ShoppingExample/ProductsLib/Interfaces/IHasExpiryDate.cs
new file mode 100644
index 0000000..9dd9796
--- /dev/null
+++ b/ShoppingExample/ProductsLib/Interfaces/IHasExpiryDate.cs
@@ -0,0 +1,9 @@
+namespace ProductsLib.Interfaces
+{
+
+ public interface IHasExpiryDate
+ {
+ DateTime ExpiryDate { get; set; }
+ bool IsExpired { get; }
+ }
+}
diff --git a/ShoppingExample/ProductsLib/Interfaces/IPhysicalOrDigital.cs b/ShoppingExample/ProductsLib/Interfaces/IPhysicalOrDigital.cs
new file mode 100644
index 0000000..56d607a
--- /dev/null
+++ b/ShoppingExample/ProductsLib/Interfaces/IPhysicalOrDigital.cs
@@ -0,0 +1,8 @@
+namespace ProductsLib.Interfaces
+{
+ public interface IPhysicalOrDigital
+ {
+ bool IsDigital { get; init; }
+ }
+
+}
diff --git a/ShoppingExample/ProductsLib/MotorVehicles/Hunday.cs b/ShoppingExample/ProductsLib/MotorVehicles/Hunday.cs
new file mode 100644
index 0000000..12cb58c
--- /dev/null
+++ b/ShoppingExample/ProductsLib/MotorVehicles/Hunday.cs
@@ -0,0 +1,12 @@
+namespace ProductsLib.MotorVehicles
+{
+ public class Hunday : MotorVehicles
+ {
+ public Hunday(string name, string description, Price price) : base(name, description, price)
+ {
+
+ }
+ }
+
+
+}
diff --git a/ShoppingExample/ProductsLib/MotorVehicles/MotorVehicles.cs b/ShoppingExample/ProductsLib/MotorVehicles/MotorVehicles.cs
new file mode 100644
index 0000000..aefb3d3
--- /dev/null
+++ b/ShoppingExample/ProductsLib/MotorVehicles/MotorVehicles.cs
@@ -0,0 +1,12 @@
+namespace ProductsLib.MotorVehicles
+{
+ public abstract class MotorVehicles : Product
+ {
+ public MotorVehicles(string name, string description, Price price)
+ : base(name, description, price)
+ {
+ }
+ }
+
+
+}
diff --git a/ShoppingExample/ProductsLib/Products.cs b/ShoppingExample/ProductsLib/Products.cs
new file mode 100644
index 0000000..2ec7ddf
--- /dev/null
+++ b/ShoppingExample/ProductsLib/Products.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection.Metadata.Ecma335;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ProductsLib
+{
+ public abstract class Product
+ {
+
+ public string Name { get; init; }
+ public string Description { get; init; }
+ public Price Price { get; init; }
+ public Product(string name, string description, Price price)
+ {
+ Name = name;
+ Description = description;
+ Price = price;
+ }
+ }
+
+ public struct Price
+ {
+ public double Amount { get; init; }
+ public string Currency { get; init; }
+ private Price(double amount, string currency)
+ {
+
+ if (amount < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(amount), "Amount Can not be negative number");
+ }
+
+
+ Amount = amount;
+ Currency = currency;
+ }
+
+ public static Price Pounds(double amount)
+ {
+
+ return new Price(amount, "GBP");
+ }
+
+ public static Price Euros(double amount)
+ {
+
+ return new Price(amount, "EUR");
+ }
+
+ public static Price USD(double amount)
+ {
+
+ return new Price(amount, "USD");
+ }
+
+
+ public static Price CAD(double amount)
+ {
+
+ return new Price(amount, "CAD");
+
+ }
+ public static Price AUD(double amount)
+ {
+ return new Price(amount, "AUD");
+ }
+
+
+ }
+
+
+}
diff --git a/ShoppingExample/ProductsLib/ProductsLib.csproj b/ShoppingExample/ProductsLib/ProductsLib.csproj
new file mode 100644
index 0000000..fa71b7a
--- /dev/null
+++ b/ShoppingExample/ProductsLib/ProductsLib.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
diff --git a/ShoppingExample/ProductsLib/Services/Services.cs b/ShoppingExample/ProductsLib/Services/Services.cs
new file mode 100644
index 0000000..db1e7c4
--- /dev/null
+++ b/ShoppingExample/ProductsLib/Services/Services.cs
@@ -0,0 +1,17 @@
+using ProductsLib.Interfaces;
+
+namespace ProductsLib.Services
+{
+ public abstract class Services : Product, IPhysicalOrDigital
+ {
+ public Services(string name, string description, Price price, bool isDigital = false)
+ : base(name, description, price)
+ {
+ IsDigital = isDigital;
+ }
+
+ public bool IsDigital { get; init; }
+ }
+
+
+}
diff --git a/ShoppingExample/ProductsLib/Tool/Tools.cs b/ShoppingExample/ProductsLib/Tool/Tools.cs
new file mode 100644
index 0000000..6716362
--- /dev/null
+++ b/ShoppingExample/ProductsLib/Tool/Tools.cs
@@ -0,0 +1,37 @@
+namespace ProductsLib.Tool
+{
+ public abstract class Tools : Product
+ {
+ public Tools(string name, string description, Price price)
+ : base(name, description, price)
+ {
+ }
+
+ public abstract bool IsHandTool { get; }
+ }
+
+ public class Hammer : Tools
+ {
+ public Hammer(string name, string description, Price price) : base(name, description, price)
+ {
+
+
+ }
+ public override bool IsHandTool => true;
+
+
+ }
+
+
+
+ public class Blender : Tools
+ {
+
+ public Blender(string name, string description, Price price) : base(name, description, price)
+ {
+
+ }
+
+ public override bool IsHandTool => false;
+ }
+}
diff --git a/ShoppingExample/ProductsLib/Voucher/TeaBagVoucher.cs b/ShoppingExample/ProductsLib/Voucher/TeaBagVoucher.cs
new file mode 100644
index 0000000..a53f0af
--- /dev/null
+++ b/ShoppingExample/ProductsLib/Voucher/TeaBagVoucher.cs
@@ -0,0 +1,12 @@
+namespace ProductsLib.Voucher
+{
+ public class TeaBagVoucher : Vouchers_Manager
+ {
+ public TeaBagVoucher(string name, string description, Price price, DateTime? expiryDate, bool isDigital = true)
+ : base(name, description, price, expiryDate, isDigital)
+ {
+ }
+ }
+
+
+}
diff --git a/ShoppingExample/ProductsLib/Voucher/Vouchers_Manager.cs b/ShoppingExample/ProductsLib/Voucher/Vouchers_Manager.cs
new file mode 100644
index 0000000..6dbb74d
--- /dev/null
+++ b/ShoppingExample/ProductsLib/Voucher/Vouchers_Manager.cs
@@ -0,0 +1,24 @@
+using ProductsLib.Interfaces;
+using ProductsLib;
+
+
+namespace ProductsLib.Voucher
+{
+ public abstract class Vouchers_Manager : Product, IHasExpiryDate, IPhysicalOrDigital
+ {
+ public Vouchers_Manager(string name, string description, Price price, DateTime? expiryDate, bool isDigital = true)
+ : base(name, description, price)
+ {
+ IsDigital = isDigital;
+ ExpiryDate = expiryDate ?? DateTime.MaxValue;
+ }
+
+ public DateTime ExpiryDate { get; set; }
+
+ public bool IsDigital { get; init; }
+
+ public bool IsExpired => DateTime.Now >= ExpiryDate;
+ }
+
+
+}
diff --git a/ShoppingExample/ShoppingExample.sln b/ShoppingExample/ShoppingExample.sln
index b232509..e16aca7 100644
--- a/ShoppingExample/ShoppingExample.sln
+++ b/ShoppingExample/ShoppingExample.sln
@@ -1,10 +1,16 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
-VisualStudioVersion = 17.14.36603.0 d17.14
+VisualStudioVersion = 17.14.36603.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp", "ConsoleApp\ConsoleApp.csproj", "{E27BCAD3-E532-440B-9CE5-50DA1B3B08FE}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProductsLib", "ProductsLib\ProductsLib.csproj", "{6B236DCE-FDFE-4377-BAA1-87535834AB0E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProductUnitTests", "ProductUnitTests\ProductUnitTests.csproj", "{4521D508-9D8D-4716-A86C-7D4C27329556}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExtensionMethodTests", "ExtensionMethodTests\ExtensionMethodTests.csproj", "{5FDE1434-6682-4E5D-A332-EF2F25F3EAA7}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -15,6 +21,18 @@ Global
{E27BCAD3-E532-440B-9CE5-50DA1B3B08FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E27BCAD3-E532-440B-9CE5-50DA1B3B08FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E27BCAD3-E532-440B-9CE5-50DA1B3B08FE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6B236DCE-FDFE-4377-BAA1-87535834AB0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6B236DCE-FDFE-4377-BAA1-87535834AB0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6B236DCE-FDFE-4377-BAA1-87535834AB0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6B236DCE-FDFE-4377-BAA1-87535834AB0E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4521D508-9D8D-4716-A86C-7D4C27329556}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4521D508-9D8D-4716-A86C-7D4C27329556}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4521D508-9D8D-4716-A86C-7D4C27329556}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4521D508-9D8D-4716-A86C-7D4C27329556}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5FDE1434-6682-4E5D-A332-EF2F25F3EAA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5FDE1434-6682-4E5D-A332-EF2F25F3EAA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5FDE1434-6682-4E5D-A332-EF2F25F3EAA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5FDE1434-6682-4E5D-A332-EF2F25F3EAA7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE