SOLID Principles
        Chris Weldon
     Improving Enterprises
Me

•   Fightin’ Texas Aggie

•   .Net and PHP Developer

•   UNIX and Windows Sysadmin

•   Senior Consultant at Improving Enterprises

•   Contact Me: chris@chrisweldon.net
Agile, Microsoft, Open Technologies, UX
Applied Training, Coaching, Mentoring
Certified Consulting
Rural Sourcing
Recruiting Services
What is OOD?
Abstraction



  What is OOD?
Abstraction   Inheritance



  What is OOD?
Abstraction     Inheritance



   What is OOD?
Encapsulation
Abstraction      Inheritance



   What is OOD?
Encapsulation   Polymorphism
What is SOLID?
?
SOLID is not a law
An Order
public class Order
{
    private int _id;
    private int _userId;
    private Product[] _products;

    public int getId() {
        return this._id;
    }

    public void setId(int id) {
        this._id = id;
    }

    public int getUserId() {
        return this._userId;
    }

    public void setUserId(int userId) {
        this._userId = userId;
    }
public Product[] getProducts() {
    return this._products;
}

public void addProduct(Product product) {
    this._products.add(product);
}

public void setProducts(Product[] products) {
    this._products = products;
}
private DBConnection _db;

public Order()
{
    // Read from configuration to get connection string.
    string connectionString = string.Empty;
    Handle fileHandle = fopen("/etc/database/app.config", "r");
    while (string row = readLine(fileHandle)) {
        if (row.StartsWith("dbo")) {
            connectionString = row;
        }
    }
    fclose(fileHandle);
    this._db = new DBConnection(connectionString);
}
public static Order CreateOrder(int userId, Product[] products)
    {
        Order order = new Order();
        order.setUserId(userId);
        order.setProducts(products);
        return order;
    }

    public static Order getOrderById(int orderId)
    {
        if (orderId == null || orderId < 0)
        {
            DBCommand command = this._db.exec("SELECT * FROM orders");
            Order[] results = this._db.getResults(command);

            // Get the first result.
            return results[0];
        }

        DBCommand command = this._db.exec("SELECT * FROM orders WHERE id
= ?", orderId);
        Order order = this._db.getSingle($command);
        return order;
    }
public void deliverOrder()
{
    try {
        WebServiceConnection networkConnection =
            new WebServiceConnection("http://shpt1/shipctrl.svc");
        networkConnection->open();
        ShippingManager shippingManager =
             new ShippingManager(networkConnection);
        shippingManager.setOrder(this);
        shippingManager.setDeliveryType(DeliveryType.EXPRESS);
        shippingManager.shipViaWebService();

        this.notifyCustomer();
    } catch (Exception e) {
        Handle logFile = fopen("/tmp/logfile.log", "a");
        fwrite("An error occurred while delivering the order.", logFile);
        fclose(logFile);
    }
}
public void notifyCustomer()
    {
        try {
            mailer = new Mailer();
            mailer.setFrom(“orders@chrisweldon.net”, “Grumpy Baby Orders”);
            mailer.setSubject(“Order #” + this.getId() + “ out for
Delivery”);
            mailer.setBodyText(“Your order is being shipped!”);
            mailer.send();
        } catch (Exception $e) {
            Handle logFile = fopen("/tmp/logfile.log", "a");
            fwrite("An error occurred while emailing the notice.", logFile);
            fclose(logFile);
        }
    }
}
OMG
Next Sample
public void ActivateDrillBit(string customerOption) {
    if (customerOption == "small") {
        SmallBit drillBit = new SmallBit();
        drillBit.activate();
    } else if (customerOption == "medium") {
        MediumBit drillBit = new MediumBit();
        drillBit.activate("120hz");
    } else if (customerOption == "large") {
        LargeBit drillBit = new LargeBit();
        drillBit.activate("240hz", Options.Water);
    }
}
Requirements Change!
 Customer Needs to Specify Options
public void ActivateDrillBit(string customerOption, string freq,
Options options) {
    if (freq == "") freq = "240hz";
    if (options == null) options = Options.NoWater;
    if (customerOption == "small") {
        SmallBit drillBit = new SmallBit();
        drillBit.activate(freq, options);
    } else if (customerOption == "medium") {
        MediumBit drillBit = new MediumBit();
        drillBit.activate(freq, options);
    } else if (customerOption == "large") {
        LargeBit drillBit = new LargeBit();
        drillBit.activate(freq, options);
    }
}
You
 Broke
  My
  App
!@#!@
public class DrillBitActivator {
    public void ActivateDrillBit(string customerOption) {
        // ...
    }
}

public class DrillBitConfigurableActivator : DrillBitActivator {
    public DrillBitConfigurableActivator(string freqency, Options options) {
        // Configurable!
    }

    public override function ActivateDrillBit(string customerOption) {
        // ...
    }
}
public function ActivateDrillBit(string customerOption)
    IDrillBit .drillBit = DrillBitFactory::CreateDrillBit(customerOption);
    drillBit.activate(this._freq, this._options);
}
Next:
A Familiar Example
public interface IManageCustomers {
    void TakeSpecifications(Specs specs);
    void ReviewSpecifications(Specs specs);
    void GiveToEngineers(Specs specs);
    void DealWithTheGoshDarnCustomers();
    bool IsPeoplePerson();
}
public class GoodManager : IManageCustomers {
    public void TakeSpecifications(Specs specs) {
        this.ReviewSpecifications(specs);
    }

    public void ReviewSpecifications(Specs specs) {
        // If specs seem good.
        this.GiveToEngineers(specs);
    }

    public void GiveToEngineers(Specs specs) {
        // E-mails specs to engineers.
    }

    public void DealWithTheGoshDarnCustomers() {
        // You better believe he does.
    }

    public bool IsPeoplePerson() {
        return true; // Absolutely!
    }
}
public class Tom : IManageCustomers {
    public void TakeSpecifications(Specs specs) {
        throw new DelegateToSecretary();
    }

    public void ReviewSpecifications(Specs specs) {
        throw new DelegateToSecretary();
    }

    public void GiveToEngineers(Specs specs) {
        throw new DelegateToSecretary();
    }

    public void DealWithTheGoshDarnCustomers() {
        // Your gosh darn right I do!
    }

    public bool IsPeoplePerson() {
        return true; // I AM a people person, dammit!
    }
}
public interface ITakeSpecifications {
    function TakeSpecifications(Specs specs);
}

public interface IReviewSpecifications {
    function ReviewSpecifications(Specs specs);
}

public interface IGiveToEngineers {
    function GiveToEngineers(Specs specs);
}

public interface IManageCustomers {
    function DealWithTheGoshDarnCustomers();
    function IsPeoplePerson();
}
public class GoodManager :
    IManageCustomers, ITakeSpecifications, IReviewSpecifications, IGiveToEngineers
{
    public void TakeSpecifications(Specs specs) {
        this.ReviewSpecifications(specs);
    }

    public void ReviewSpecifications(Specs specs) {
        // If specs seem good.
        this.GiveToEngineers(specs);
    }

    public void GiveToEngineers(Specs $specs) {
        // E-mails specs to engineers.
    }

    public void DealWithTheGoshDarnCustomers() {
        // You better believe he does.
    }

    public bool IsPeoplePerson() {
        return true; // Absolutely!
    }
}
public class Tom : IManageCustomers {
    public void DealWithTheGoshDarnCustomers() {
        // Your gosh darn right I do!
    }

    public bool IsPeoplePerson() {
        return true; // I AM a people person, dammit!
    }
}
Next Up
public interface IDataResource
{
    void Load();
    void Save();
}
public class AppSettings : IDataResource {
    public void Load() {
        // Load application settings.
    }

    public void Save() {
        // Save application settings.
    }
}
public class UserSettings : IDataResource {
    public void Load() {
        // Load user settings.
    }

    public void Save() {
        // Save user settings.
    }
}
static IDataResource[] LoadAll() {
    IDataResource[] resources = new IDataResource[2] {
        new AppSettings(),
        new UserSettings()
    };

    foreach (IDataResource resource in resources) {
        resource.Load();
    }

    return resources;
}

static void SaveAll(IDataResource[] resources) {
    foreach (IDataResource resource in resources) {
        resource.Save();
    }
}
Our “Duck”
public class UnsaveableSettings : AppSettings {
    public override void Load() {
        // Loads settings.
    }

    public override void Save() {
        throw new CannotSaveException();
    }
}
static IDataResource[] LoadAll() {
    IDataResource[] resources = new IDataResource[3] {
        new AppSettings(),
        new UserSettings(),
        new UnsaveableSettings()
    };

    foreach (IDataResource resource in resources) {
        resource.Load();
    }

    return resources;
}

static void SaveAll(IDataResource[] resources) {
    foreach (IDataResource resource in resources) {
        if (resource is UnsaveableSettings) continue;

        resource.Save();
    }
}
public interface IDataResource
{
    void Load();
}

public interface ISaveResource
{
    void Save();
}
public class AppSettingsLoaderBase : IDataResource {
    public function Load() {
        // Load application settings.
    }
}
public class AppSettings extends AppSettingsLoaderBase
    implements ISaveResource {
    public function Save() {
        // Save application settings.
    }
}
public class UnsaveableSettings extends AppSettingsLoaderBase {
    public override void Load() {
        // Loads settings.
    }
}
static IDataResource[] LoadAll() {
    IDataResource[] resources = new IDataResource[3] {
        new AppSettings(),
        new UserSettings(),
        new UnsaveableSettings()
    };

    foreach (IDataResource resource in resources) {
        resource.Load();
    }

    return resources;
}

static void SaveAll(ISaveResource[] resources) {
    foreach (ISaveResource resource in resources) {
        resource.Save();
    }
}
Final Problem
public class Authenticator {
    private DataAccessLayer _repository;

    public DataAccessLayer() {
        this._repository = new DataAccessLayer();
    }

    public bool authenticate(string username, string password) {
        string hashedPassword = md5(password);
        User user = this._repository.findByUsernameAndPassword(
            username, hashedPassword);
        return user == null;
    }
}
Problems?
Problems?
          Authenticator
      authenticate() : bool




        DataAccessLayer
findByUsernameAndPassword : array
Problems?
                Authenticator
            authenticate() : bool




              DataAccessLayer
      findByUsernameAndPassword : array



Strongly coupled to DataAccessLayer
class Authenticator {
    private DataAccessLayer _repository;

    public Authenticator(DataAccessLayer repository) {
        this._repository = repository;
    }

    public bool authenticate(string username, string password) {
        string hashedPassword = md5(password);
        User user = this._repository.findByUsernameAndPassword(
            username, hashedPassword);
        return user == null;
    }
}
Dependency Inversion


•   “High-level modules should not depend upon low level modules. They
    should depend upon abstractions.

•   “Abstractions should not depend upon details. Details should depend
    upon abstractions.”
                                                           Robert Martin
Combine Principles!
public interface IUserRepository {
    User findByUsernameAndPassword(string username, string password);
}
public class DataAccessLayer : IUserRepository {
    public User findByUsernameAndPassword(string username, string password) {
        // Do some database stuff.
    }
}
class Authenticator {
    private IUserRepository _repository;

    public Authenticator(IUserRepository repository) {
        this._repository = repository;
    }

    public bool authenticate(string username, string password) {
        string hashedPassword = md5(password);
        User user = this._repository.findByUsernameAndPassword(
            username, hashedPassword);
        return user == null;
    }
}
Comparison
Comparison

          Authenticator
      authenticate() : bool




        DataAccessLayer
findByUsernameAndPassword : array
Comparison

          Authenticator
      authenticate() : bool




        DataAccessLayer
findByUsernameAndPassword : array
Comparison
                                                 Authenticator
                                             authenticate() : bool

          Authenticator
      authenticate() : bool

                                                IUserRepository
                                       findByUsernameAndPassword : array

        DataAccessLayer
findByUsernameAndPassword : array

                                               DataAccessLayer
                                       findByUsernameAndPassword : array
Benefit: Flexibility
public class WebServiceUserRepository : IUserRepository {
    public User findByUsernameAndPassword(string username, string password) {
        // Fetch our user through JSON or SOAP
    }
}

public class OAuthRepository : IUserRepository {
    public User findByUsernameAndPassword(string username, string password) {
        // Connect to your favorite OAuth provider
    }
}
Thank You!



Check improvingaggies.com   http://spkr8.com/neraath

SOLID Principles

  • 1.
    SOLID Principles Chris Weldon Improving Enterprises
  • 2.
    Me • Fightin’ Texas Aggie • .Net and PHP Developer • UNIX and Windows Sysadmin • Senior Consultant at Improving Enterprises • Contact Me: chris@chrisweldon.net
  • 3.
    Agile, Microsoft, OpenTechnologies, UX Applied Training, Coaching, Mentoring Certified Consulting Rural Sourcing Recruiting Services
  • 4.
  • 5.
  • 6.
    Abstraction Inheritance What is OOD?
  • 7.
    Abstraction Inheritance What is OOD? Encapsulation
  • 8.
    Abstraction Inheritance What is OOD? Encapsulation Polymorphism
  • 9.
  • 10.
  • 11.
  • 13.
  • 14.
    public class Order { private int _id; private int _userId; private Product[] _products; public int getId() { return this._id; } public void setId(int id) { this._id = id; } public int getUserId() { return this._userId; } public void setUserId(int userId) { this._userId = userId; }
  • 15.
    public Product[] getProducts(){ return this._products; } public void addProduct(Product product) { this._products.add(product); } public void setProducts(Product[] products) { this._products = products; }
  • 16.
    private DBConnection _db; publicOrder() { // Read from configuration to get connection string. string connectionString = string.Empty; Handle fileHandle = fopen("/etc/database/app.config", "r"); while (string row = readLine(fileHandle)) { if (row.StartsWith("dbo")) { connectionString = row; } } fclose(fileHandle); this._db = new DBConnection(connectionString); }
  • 17.
    public static OrderCreateOrder(int userId, Product[] products) { Order order = new Order(); order.setUserId(userId); order.setProducts(products); return order; } public static Order getOrderById(int orderId) { if (orderId == null || orderId < 0) { DBCommand command = this._db.exec("SELECT * FROM orders"); Order[] results = this._db.getResults(command); // Get the first result. return results[0]; } DBCommand command = this._db.exec("SELECT * FROM orders WHERE id = ?", orderId); Order order = this._db.getSingle($command); return order; }
  • 18.
    public void deliverOrder() { try { WebServiceConnection networkConnection = new WebServiceConnection("http://shpt1/shipctrl.svc"); networkConnection->open(); ShippingManager shippingManager = new ShippingManager(networkConnection); shippingManager.setOrder(this); shippingManager.setDeliveryType(DeliveryType.EXPRESS); shippingManager.shipViaWebService(); this.notifyCustomer(); } catch (Exception e) { Handle logFile = fopen("/tmp/logfile.log", "a"); fwrite("An error occurred while delivering the order.", logFile); fclose(logFile); } }
  • 19.
    public void notifyCustomer() { try { mailer = new Mailer(); mailer.setFrom(“orders@chrisweldon.net”, “Grumpy Baby Orders”); mailer.setSubject(“Order #” + this.getId() + “ out for Delivery”); mailer.setBodyText(“Your order is being shipped!”); mailer.send(); } catch (Exception $e) { Handle logFile = fopen("/tmp/logfile.log", "a"); fwrite("An error occurred while emailing the notice.", logFile); fclose(logFile); } } }
  • 21.
  • 23.
  • 24.
    public void ActivateDrillBit(stringcustomerOption) { if (customerOption == "small") { SmallBit drillBit = new SmallBit(); drillBit.activate(); } else if (customerOption == "medium") { MediumBit drillBit = new MediumBit(); drillBit.activate("120hz"); } else if (customerOption == "large") { LargeBit drillBit = new LargeBit(); drillBit.activate("240hz", Options.Water); } }
  • 25.
    Requirements Change! CustomerNeeds to Specify Options
  • 26.
    public void ActivateDrillBit(stringcustomerOption, string freq, Options options) { if (freq == "") freq = "240hz"; if (options == null) options = Options.NoWater; if (customerOption == "small") { SmallBit drillBit = new SmallBit(); drillBit.activate(freq, options); } else if (customerOption == "medium") { MediumBit drillBit = new MediumBit(); drillBit.activate(freq, options); } else if (customerOption == "large") { LargeBit drillBit = new LargeBit(); drillBit.activate(freq, options); } }
  • 28.
    You Broke My App !@#!@
  • 30.
    public class DrillBitActivator{ public void ActivateDrillBit(string customerOption) { // ... } } public class DrillBitConfigurableActivator : DrillBitActivator { public DrillBitConfigurableActivator(string freqency, Options options) { // Configurable! } public override function ActivateDrillBit(string customerOption) { // ... } }
  • 31.
    public function ActivateDrillBit(stringcustomerOption) IDrillBit .drillBit = DrillBitFactory::CreateDrillBit(customerOption); drillBit.activate(this._freq, this._options); }
  • 32.
  • 33.
    public interface IManageCustomers{ void TakeSpecifications(Specs specs); void ReviewSpecifications(Specs specs); void GiveToEngineers(Specs specs); void DealWithTheGoshDarnCustomers(); bool IsPeoplePerson(); }
  • 34.
    public class GoodManager: IManageCustomers { public void TakeSpecifications(Specs specs) { this.ReviewSpecifications(specs); } public void ReviewSpecifications(Specs specs) { // If specs seem good. this.GiveToEngineers(specs); } public void GiveToEngineers(Specs specs) { // E-mails specs to engineers. } public void DealWithTheGoshDarnCustomers() { // You better believe he does. } public bool IsPeoplePerson() { return true; // Absolutely! } }
  • 36.
    public class Tom: IManageCustomers { public void TakeSpecifications(Specs specs) { throw new DelegateToSecretary(); } public void ReviewSpecifications(Specs specs) { throw new DelegateToSecretary(); } public void GiveToEngineers(Specs specs) { throw new DelegateToSecretary(); } public void DealWithTheGoshDarnCustomers() { // Your gosh darn right I do! } public bool IsPeoplePerson() { return true; // I AM a people person, dammit! } }
  • 38.
    public interface ITakeSpecifications{ function TakeSpecifications(Specs specs); } public interface IReviewSpecifications { function ReviewSpecifications(Specs specs); } public interface IGiveToEngineers { function GiveToEngineers(Specs specs); } public interface IManageCustomers { function DealWithTheGoshDarnCustomers(); function IsPeoplePerson(); }
  • 39.
    public class GoodManager: IManageCustomers, ITakeSpecifications, IReviewSpecifications, IGiveToEngineers { public void TakeSpecifications(Specs specs) { this.ReviewSpecifications(specs); } public void ReviewSpecifications(Specs specs) { // If specs seem good. this.GiveToEngineers(specs); } public void GiveToEngineers(Specs $specs) { // E-mails specs to engineers. } public void DealWithTheGoshDarnCustomers() { // You better believe he does. } public bool IsPeoplePerson() { return true; // Absolutely! } }
  • 40.
    public class Tom: IManageCustomers { public void DealWithTheGoshDarnCustomers() { // Your gosh darn right I do! } public bool IsPeoplePerson() { return true; // I AM a people person, dammit! } }
  • 41.
  • 42.
    public interface IDataResource { void Load(); void Save(); }
  • 43.
    public class AppSettings: IDataResource { public void Load() { // Load application settings. } public void Save() { // Save application settings. } }
  • 44.
    public class UserSettings: IDataResource { public void Load() { // Load user settings. } public void Save() { // Save user settings. } }
  • 45.
    static IDataResource[] LoadAll(){ IDataResource[] resources = new IDataResource[2] { new AppSettings(), new UserSettings() }; foreach (IDataResource resource in resources) { resource.Load(); } return resources; } static void SaveAll(IDataResource[] resources) { foreach (IDataResource resource in resources) { resource.Save(); } }
  • 46.
  • 47.
    public class UnsaveableSettings: AppSettings { public override void Load() { // Loads settings. } public override void Save() { throw new CannotSaveException(); } }
  • 48.
    static IDataResource[] LoadAll(){ IDataResource[] resources = new IDataResource[3] { new AppSettings(), new UserSettings(), new UnsaveableSettings() }; foreach (IDataResource resource in resources) { resource.Load(); } return resources; } static void SaveAll(IDataResource[] resources) { foreach (IDataResource resource in resources) { if (resource is UnsaveableSettings) continue; resource.Save(); } }
  • 50.
    public interface IDataResource { void Load(); } public interface ISaveResource { void Save(); }
  • 51.
    public class AppSettingsLoaderBase: IDataResource { public function Load() { // Load application settings. } }
  • 52.
    public class AppSettingsextends AppSettingsLoaderBase implements ISaveResource { public function Save() { // Save application settings. } }
  • 53.
    public class UnsaveableSettingsextends AppSettingsLoaderBase { public override void Load() { // Loads settings. } }
  • 54.
    static IDataResource[] LoadAll(){ IDataResource[] resources = new IDataResource[3] { new AppSettings(), new UserSettings(), new UnsaveableSettings() }; foreach (IDataResource resource in resources) { resource.Load(); } return resources; } static void SaveAll(ISaveResource[] resources) { foreach (ISaveResource resource in resources) { resource.Save(); } }
  • 55.
  • 56.
    public class Authenticator{ private DataAccessLayer _repository; public DataAccessLayer() { this._repository = new DataAccessLayer(); } public bool authenticate(string username, string password) { string hashedPassword = md5(password); User user = this._repository.findByUsernameAndPassword( username, hashedPassword); return user == null; } }
  • 57.
  • 58.
    Problems? Authenticator authenticate() : bool DataAccessLayer findByUsernameAndPassword : array
  • 59.
    Problems? Authenticator authenticate() : bool DataAccessLayer findByUsernameAndPassword : array Strongly coupled to DataAccessLayer
  • 61.
    class Authenticator { private DataAccessLayer _repository; public Authenticator(DataAccessLayer repository) { this._repository = repository; } public bool authenticate(string username, string password) { string hashedPassword = md5(password); User user = this._repository.findByUsernameAndPassword( username, hashedPassword); return user == null; } }
  • 62.
    Dependency Inversion • “High-level modules should not depend upon low level modules. They should depend upon abstractions. • “Abstractions should not depend upon details. Details should depend upon abstractions.” Robert Martin
  • 63.
  • 64.
    public interface IUserRepository{ User findByUsernameAndPassword(string username, string password); } public class DataAccessLayer : IUserRepository { public User findByUsernameAndPassword(string username, string password) { // Do some database stuff. } }
  • 65.
    class Authenticator { private IUserRepository _repository; public Authenticator(IUserRepository repository) { this._repository = repository; } public bool authenticate(string username, string password) { string hashedPassword = md5(password); User user = this._repository.findByUsernameAndPassword( username, hashedPassword); return user == null; } }
  • 66.
  • 67.
    Comparison Authenticator authenticate() : bool DataAccessLayer findByUsernameAndPassword : array
  • 68.
    Comparison Authenticator authenticate() : bool DataAccessLayer findByUsernameAndPassword : array
  • 69.
    Comparison Authenticator authenticate() : bool Authenticator authenticate() : bool IUserRepository findByUsernameAndPassword : array DataAccessLayer findByUsernameAndPassword : array DataAccessLayer findByUsernameAndPassword : array
  • 70.
    Benefit: Flexibility public classWebServiceUserRepository : IUserRepository { public User findByUsernameAndPassword(string username, string password) { // Fetch our user through JSON or SOAP } } public class OAuthRepository : IUserRepository { public User findByUsernameAndPassword(string username, string password) { // Connect to your favorite OAuth provider } }
  • 71.
    Thank You! Check improvingaggies.com http://spkr8.com/neraath