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