Open Closed Principle (OCP) – S.O.L.I.D. Framework
The second principle of the S.O.L.I.D. Framework is the Open Closed Principle (OCP). At the very first moment the combination of open and closed at the same time creates some confusion, but after we dig deeper it makes sense.
The main statement of this principle is “A software module is open for extension and closed for modification“.
So, at writing software we should make sure that an class/module is open for extension but closed for any modifications/changes. Of course there is one exception – bug fixing :-).
If we take a look at the example from the first principle (SRP) and we want to add a method to the AccountManager class – e.g. Unregister, we usually would add this method directly under the Register method, but this would violate the OCP!
public class AccountManager { EmailService emailservice; public AccountManager(EmailService emailservice) { this.emailservice = emailservice; } public void Register(string name, string email, string password) { Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(name)); Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(email)); Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(password)); var account = new Account(name, email, password); this.emailservice.SendEmail(new MailMessage("confirmation@registration.com", email) { Subject = "Confirmation of your Registration." }); } } public class EmailService { SmtpClient smtpClient; public EmailService(SmtpClient smtpClient) { this.smtpClient = smtpClient; } public void SendEmail(MailMessage message) { smtpClient.Send(message); } }
Instead we can use inheritance to match the OCP defintion. Our extended program would somehow look like this:
{ public EmailService emailservice; public AccountManager(EmailService emailservice) { this.emailservice = emailservice; } public void Register(string name, string email, string password) { Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(name)); Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(email)); Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(password)); var account = new Account(name, email, password); this.emailservice.SendEmail(new MailMessage("confirmation@registration.com", email) { Subject = "Confirmation of your Registration." }); } } public class AccountManagerExtended : AccountManager { public AccountManagerExtended(EmailService emailservice) : base(emailservice) { } public void Unregister(Account account) { Contract.Requires<ArgumentException>(account != null); this.emailservice.SendEmail(new MailMessage("confirmation@unregistration.com", account.email) { Subject = "Confirmation of your Unregistration." }); } } public class EmailService { SmtpClient smtpClient; public EmailService(SmtpClient smtpClient) { this.smtpClient = smtpClient; } public void SendEmail(MailMessage message) { smtpClient.Send(message); } }
This is one way to be compliant to the OCP and therefore the S.O.L.I.D. Framework.