Abstract V.S. Virtual
Abstract:
An abstract class/method MUST be implemented by derived classes
Abstract classes cannot be instantiated directly
Classes with abstract methods must be declared abstract
Abstract methods have no implementation in the base class
Virtual:
A virtual method CAN be overridden by derived classes
Virtual methods have a default implementation in the base class
Classes with virtual methods can be instantiated
Derived classes may choose to override or use the base implementation
SOLID
Single responsibility principle(SRP) 單一職責 「單一職責」原則顧名思義,就是一個類別應該只負責一個職責。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public void CreateOrder () { ReceiveOrder(); WriteInOrder(); GetInformation(); SendNotification(); } public class OrderReceiver { public void ReceiveOrder () { } } public class OrderWriter { public void WriteOrder () { } } public class InformationGetter { public void GetInformation () { } } public class NotificationSender { public void SendNotification () { } }
Open/close principle(OCP) 開放/封閉原則 軟體實體(類別、模組、函式等等)應該對擴展開放,而對修改封閉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 public abstract class Shape { public abstract double CalculateArea () ; } public class Rectangle : Shape { private double width; private double height; public Rectangle (double width, double height ) { this .width = width; this .height = height; } public override double CalculateArea () { return width * height; } } public class Circle : Shape { private double radius; public Circle (double radius ) { this .radius = radius; } public override double CalculateArea () { return Math.PI * radius * radius; } } public class Triangle : Shape { private double baseLength; private double height; public Triangle (double baseLength, double height ) { this .baseLength = baseLength; this .height = height; } public override double CalculateArea () { return 0.5 * baseLength * height; } } public class AreaCalculator { public double Calculate (Shape shape ) { return shape.CalculateArea(); } } var calculator = new AreaCalculator();var rectangle = new Rectangle(10 , 5 );var circle = new Circle(7 );var triangle = new Triangle(6 , 4 );Console.WriteLine($"Rectangle Area: {calculator.Calculate(rectangle)} " ); Console.WriteLine($"Circle Area: {calculator.Calculate(circle)} " ); Console.WriteLine($"Triangle Area: {calculator.Calculate(triangle)} " );
Liskov substitution principle(LSP) Liskov替換 如果 S 是 T 的子類型,那麼在不改變程式正確性的前提下,我們應該能夠將所有類型為 T 的對象都替換成類型為 S 的對象。簡單說,子類別必須能夠完全替換其父類別,而不會導致程式出現異常行為。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 public interface IBird { void Move () ; void MakeSound () ; } public interface IFlyingBird : IBird { void Fly () ; } public class Penguin : IBird { public void Move () { Console.WriteLine("Penguin is walking" ); } public void MakeSound () { Console.WriteLine("Penguin makes a sound" ); } } public class Sparrow : IFlyingBird { public void Move () { Console.WriteLine("Sparrow is moving" ); } public void MakeSound () { Console.WriteLine("Sparrow chirps" ); } public void Fly () { Console.WriteLine("Sparrow is flying" ); } } public class BirdManager { public void MakeBirdMove (IBird bird ) { bird.Move(); bird.MakeSound(); } public void MakeBirdFly (IFlyingBird bird ) { bird.Fly(); } } public abstract class Bird { public abstract void Fly () ; } public class Ostrich : Bird { public override void Fly () { throw new NotImplementedException("Ostriches can't fly!" ); } }
Interface Segregation Principle(ISP) 介面隔離
介面隔離原則 (ISP) 是指不應該強迫客戶端依賴它不需要的介面。換句話說,一個類別不應該被強迫實作它用不到的方法。這個原則建議將大型介面分割成更小、更具體的介面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 public interface IAllInOnePrinter { void Print (string document ) ; void Scan (string document ) ; void Fax (string document ) ; void PrintDuplex (string document ) ; void ScanColor (string document ) ; void ScanDuplex (string document ) ; } public class BasicPrinter : IAllInOnePrinter { public void Print (string document ) { Console.WriteLine("Printing document" ); } public void Scan (string document ) { throw new NotImplementedException("Basic printer cannot scan!" ); } public void Fax (string document ) { throw new NotImplementedException("Basic printer cannot fax!" ); } public void PrintDuplex (string document ) { throw new NotImplementedException("Basic printer cannot print duplex!" ); } public void ScanColor (string document ) { throw new NotImplementedException("Basic printer cannot scan in color!" ); } public void ScanDuplex (string document ) { throw new NotImplementedException("Basic printer cannot scan duplex!" ); } } public interface IPrinter { void Print (string document ) ; } public interface IScanner { void Scan (string document ) ; } public interface IFax { void Fax (string document ) ; } public interface IDuplexPrinter { void PrintDuplex (string document ) ; } public interface IColorScanner { void ScanColor (string document ) ; } public interface IDuplexScanner { void ScanDuplex (string document ) ; }
Dependency Inversion Principle(DIP) 依賴反轉
依賴反轉原則有兩個核心概念:
高層模組不應依賴低層模組,兩者都應該依賴於抽象
抽象不應該依賴於細節,細節應該依賴於抽象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 public class EmailService { public void SendEmail (string message ) { Console.WriteLine($"Sending email: {message} " ); } } public class NotificationService { private EmailService emailService; public NotificationService () { emailService = new EmailService(); } public void SendNotification (string message ) { emailService.SendEmail(message); } } public interface IMessageService { void SendMessage (string message ) ; } public class EmailServiceImpl : IMessageService { public void SendMessage (string message ) { Console.WriteLine($"Sending email: {message} " ); } } public class SMSServiceImpl : IMessageService { public void SendMessage (string message ) { Console.WriteLine($"Sending SMS: {message} " ); } } public class SlackServiceImpl : IMessageService { public void SendMessage (string message ) { Console.WriteLine($"Sending Slack message: {message} " ); } } public class NotificationManager { private readonly IMessageService _messageService; public NotificationManager (IMessageService messageService ) { _messageService = messageService; } public void SendNotification (string message ) { _messageService.SendMessage(message); } } public class MessageServiceFactory { public static IMessageService CreateMessageService (string type ) { return type.ToLower() switch { "email" => new EmailServiceImpl(), "sms" => new SMSServiceImpl(), "slack" => new SlackServiceImpl(), _ => throw new ArgumentException("Unknown message service type" ) }; } } public class DependencyContainer { private readonly Dictionary<Type, Type> _dependencies = new Dictionary<Type, Type>(); public void Register <TInterface , TImplementation >() where TImplementation : TInterface { _dependencies[typeof (TInterface)] = typeof (TImplementation); } public T Resolve <T >() { Type type = typeof (T); if (_dependencies.ContainsKey(type)) { return (T)Activator.CreateInstance(_dependencies[type]); } throw new Exception($"No registration found for {type} " ); } }