├── .gitignore ├── open_closed ├── good │ ├── src │ │ ├── Personality.java │ │ ├── CasualPersonality.java │ │ ├── FormalPersonality.java │ │ ├── IntimatePersonality.java │ │ └── Greeter.java │ └── spec │ │ ├── CasualPersonalityTest.java │ │ ├── FormalPersonalityTest.java │ │ ├── IntimatePersonalityTest.java │ │ └── GreeterTest.java ├── bad │ ├── src │ │ └── Greeter.java │ └── spec │ │ └── GreeterTest.java └── README.md ├── interface_segregation ├── good │ ├── src │ │ ├── FlyingCreature.java │ │ ├── FeatheredCreature.java │ │ ├── SwimmingCreature.java │ │ ├── Eagle.java │ │ └── Penguin.java │ └── spec │ │ ├── EagleTest.java │ │ └── PenguinTest.java ├── bad │ ├── src │ │ ├── Bird.java │ │ ├── Eagle.java │ │ └── Penguin.java │ └── spec │ │ ├── EagleTest.java │ │ └── PenguinTest.java └── README.md ├── dependency_inversion ├── good │ ├── src │ │ ├── Notifier.java │ │ ├── EmailClient.java │ │ ├── MobileDevice.java │ │ └── WeatherTracker.java │ └── spec │ │ ├── EmailClientTest.java │ │ ├── MobileDeviceTest.java │ │ └── WeatherTrackerTest.java ├── bad │ ├── src │ │ ├── Phone.java │ │ ├── Emailer.java │ │ └── WeatherTracker.java │ └── spec │ │ ├── PhoneTest.java │ │ ├── EmailerTest.java │ │ └── WeatherTrackerTest.java └── README.md ├── liskov_substitution ├── good │ ├── src │ │ ├── BedroomAdder.java │ │ ├── Studio.java │ │ └── PenthouseSuite.java │ └── spec │ │ ├── BedroomAdderTest.java │ │ ├── StudioTest.java │ │ └── PenthouseSuiteTest.java ├── bad │ ├── src │ │ ├── Apartment.java │ │ ├── Studio.java │ │ ├── PenthouseSuite.java │ │ └── UnitUpgrader.java │ └── spec │ │ ├── StudioTest.java │ │ ├── PenthouseSuiteTest.java │ │ └── UnitUpgraderTest.java └── README.md ├── README.md └── single_responsibility ├── good ├── spec │ ├── BoardPresenterTest.java │ ├── BoardShaperTest.java │ └── BoardTest.java └── src │ ├── BoardShaper.java │ ├── BoardPresenter.java │ └── Board.java ├── README.md └── bad ├── src └── Board.java └── spec └── BoardTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | out/ 3 | *.iml 4 | -------------------------------------------------------------------------------- /open_closed/good/src/Personality.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/7/14. 3 | */ 4 | public interface Personality { 5 | public String greet(); 6 | } 7 | -------------------------------------------------------------------------------- /interface_segregation/good/src/FlyingCreature.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/7/14. 3 | */ 4 | public interface FlyingCreature { 5 | public void fly(); 6 | } 7 | -------------------------------------------------------------------------------- /interface_segregation/bad/src/Bird.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/7/14. 3 | */ 4 | public interface Bird { 5 | public void fly(); 6 | public void molt(); 7 | } 8 | -------------------------------------------------------------------------------- /interface_segregation/good/src/FeatheredCreature.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/7/14. 3 | */ 4 | public interface FeatheredCreature { 5 | public void molt(); 6 | } 7 | -------------------------------------------------------------------------------- /interface_segregation/good/src/SwimmingCreature.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/7/14. 3 | */ 4 | public interface SwimmingCreature { 5 | public void swim(); 6 | } 7 | -------------------------------------------------------------------------------- /dependency_inversion/good/src/Notifier.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/8/14. 3 | */ 4 | interface Notifier { 5 | public void alertWeatherConditions(String weatherConditions); 6 | } 7 | -------------------------------------------------------------------------------- /open_closed/good/src/CasualPersonality.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/7/14. 3 | */ 4 | public class CasualPersonality implements Personality { 5 | public String greet() { 6 | return "Sup bro?"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /open_closed/good/src/FormalPersonality.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/7/14. 3 | */ 4 | public class FormalPersonality implements Personality { 5 | public String greet() { 6 | return "Good evening, sir."; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /open_closed/good/src/IntimatePersonality.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/7/14. 3 | */ 4 | public class IntimatePersonality implements Personality { 5 | public String greet() { 6 | return "Hello Darling!"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /liskov_substitution/good/src/BedroomAdder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/8/14. 3 | */ 4 | public class BedroomAdder { 5 | public void addBedroom(PenthouseSuite penthouse) { 6 | penthouse.numberOfBedrooms += 1; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /liskov_substitution/bad/src/Apartment.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/8/14. 3 | */ 4 | abstract class Apartment { 5 | int squareFootage; 6 | int numberOfBedrooms; 7 | 8 | abstract void setSquareFootage(int sqft); 9 | } 10 | -------------------------------------------------------------------------------- /dependency_inversion/bad/src/Phone.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/8/14. 3 | */ 4 | public class Phone { 5 | public String generateWeatherAlert(String weatherConditions) { 6 | String alert = "It is " + weatherConditions; 7 | return alert; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /dependency_inversion/bad/src/Emailer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/8/14. 3 | */ 4 | public class Emailer { 5 | public String generateWeatherAlert(String weatherConditions) { 6 | String alert = "It is " + weatherConditions; 7 | return alert; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /liskov_substitution/bad/src/Studio.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/8/14. 3 | */ 4 | public class Studio extends Apartment { 5 | public Studio() { 6 | this.numberOfBedrooms = 0; 7 | } 8 | 9 | public void setSquareFootage(int sqft) { 10 | this.squareFootage = sqft; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /dependency_inversion/good/src/EmailClient.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/8/14. 3 | */ 4 | public class EmailClient implements Notifier { 5 | public void alertWeatherConditions(String weatherConditions) { 6 | if (weatherConditions == "sunny"); 7 | System.out.print("It is sunny"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /dependency_inversion/good/src/MobileDevice.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/8/14. 3 | */ 4 | public class MobileDevice implements Notifier { 5 | public void alertWeatherConditions(String weatherConditions) { 6 | if (weatherConditions == "rainy") 7 | System.out.print("It is rainy"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /liskov_substitution/bad/src/PenthouseSuite.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/8/14. 3 | */ 4 | public class PenthouseSuite extends Apartment { 5 | public PenthouseSuite() { 6 | this.numberOfBedrooms = 4; 7 | } 8 | 9 | public void setSquareFootage(int sqft) { 10 | this.squareFootage = sqft; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /liskov_substitution/bad/src/UnitUpgrader.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/8/14. 3 | */ 4 | public class UnitUpgrader { 5 | public void upgrade(Apartment apartment) { 6 | apartment.squareFootage += 40; 7 | 8 | if (apartment.getClass() != Studio.class) 9 | apartment.numberOfBedrooms += 1; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /liskov_substitution/good/src/Studio.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/8/14. 3 | */ 4 | public class Studio { 5 | int squareFootage; 6 | int numberOfRooms; 7 | 8 | public Studio() { 9 | this.numberOfRooms = 0; 10 | } 11 | 12 | public void setSquareFootage(int sqft) { 13 | this.squareFootage = sqft; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /open_closed/good/src/Greeter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/7/14. 3 | */ 4 | public class Greeter { 5 | private Personality personality; 6 | 7 | public Greeter(Personality personality) { 8 | this.personality = personality; 9 | } 10 | 11 | public String greet() { 12 | return this.personality.greet(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /liskov_substitution/good/src/PenthouseSuite.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/8/14. 3 | */ 4 | public class PenthouseSuite { 5 | int squareFootage; 6 | int numberOfBedrooms; 7 | 8 | public PenthouseSuite() { 9 | this.numberOfBedrooms = 4; 10 | } 11 | 12 | public void setSquareFootage(int sqft) { 13 | this.squareFootage = sqft; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /dependency_inversion/bad/spec/PhoneTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | /** 6 | * Created by mrk on 4/8/14. 7 | */ 8 | public class PhoneTest { 9 | @Test 10 | public void testGeneratesAlertString() { 11 | Phone phone = new Phone(); 12 | assertEquals("It is rainy", phone.generateWeatherAlert("rainy")); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /dependency_inversion/bad/spec/EmailerTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | /** 6 | * Created by mrk on 4/8/14. 7 | */ 8 | public class EmailerTest { 9 | @Test 10 | public void testGeneratesAlertString() { 11 | Emailer emailer = new Emailer(); 12 | assertEquals("It is sunny", emailer.generateWeatherAlert("sunny")); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /open_closed/good/spec/CasualPersonalityTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | /** 6 | * Created by mrk on 4/7/14. 7 | */ 8 | public class CasualPersonalityTest { 9 | @Test 10 | public void testGreetsSomeoneCasually() { 11 | CasualPersonality cp = new CasualPersonality(); 12 | assertEquals("Sup bro?", cp.greet()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /open_closed/good/spec/FormalPersonalityTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | /** 6 | * Created by mrk on 4/7/14. 7 | */ 8 | public class FormalPersonalityTest { 9 | @Test 10 | public void testGreetsSomeoneFormally() { 11 | FormalPersonality fp = new FormalPersonality(); 12 | assertEquals("Good evening, sir.", fp.greet()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /open_closed/good/spec/IntimatePersonalityTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | /** 6 | * Created by mrk on 4/7/14. 7 | */ 8 | public class IntimatePersonalityTest { 9 | @Test 10 | public void testGreetsSomeoneIntimately() { 11 | IntimatePersonality ip = new IntimatePersonality(); 12 | assertEquals("Hello Darling!", ip.greet()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /dependency_inversion/good/src/WeatherTracker.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/8/14. 3 | */ 4 | public class WeatherTracker { 5 | String currentConditions; 6 | 7 | public void setCurrentConditions(String weatherDescription) { 8 | this.currentConditions = weatherDescription; 9 | } 10 | 11 | public void notify(Notifier notifier) { 12 | notifier.alertWeatherConditions(currentConditions); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SOLID design principles 2 | 3 | This repository contains examples of the five SOLID design principles of object-oriented programming. The examples are written in Java. Each example has a "good" and "bad" version to demonstrate adherence to and violation of the principle, respectively. 4 | 5 | - Single Responsibility Principle 6 | - Open/Closed Principle 7 | - Liskov Substitution Principle 8 | - Interface Segregation Principle 9 | - Dependency Inversion Principle 10 | -------------------------------------------------------------------------------- /interface_segregation/bad/src/Eagle.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/7/14. 3 | */ 4 | public class Eagle implements Bird { 5 | String currentLocation; 6 | int numberOfFeathers; 7 | 8 | public Eagle(int initialFeatherCount) { 9 | this.numberOfFeathers = initialFeatherCount; 10 | } 11 | 12 | public void fly() { 13 | this.currentLocation = "in the air"; 14 | } 15 | 16 | public void molt() { 17 | this.numberOfFeathers -= 1; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /liskov_substitution/good/spec/BedroomAdderTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | /** 6 | * Created by mrk on 4/8/14. 7 | */ 8 | public class BedroomAdderTest { 9 | @Test 10 | public void testAddsBedroomToPenthouse() { 11 | PenthouseSuite penthouse = new PenthouseSuite(); 12 | BedroomAdder adder = new BedroomAdder(); 13 | adder.addBedroom(penthouse); 14 | 15 | assertEquals(5, penthouse.numberOfBedrooms); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /interface_segregation/good/src/Eagle.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/7/14. 3 | */ 4 | public class Eagle implements FlyingCreature, FeatheredCreature { 5 | String currentLocation; 6 | int numberOfFeathers; 7 | 8 | public Eagle(int initialNumberOfFeathers) { 9 | this.numberOfFeathers = initialNumberOfFeathers; 10 | } 11 | 12 | public void fly() { 13 | this.currentLocation = "in the air"; 14 | } 15 | 16 | public void molt() { 17 | this.numberOfFeathers -= 1; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /interface_segregation/good/src/Penguin.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/7/14. 3 | */ 4 | public class Penguin implements SwimmingCreature, FeatheredCreature { 5 | String currentLocation; 6 | int numberOfFeathers; 7 | 8 | public Penguin(int initialFeatherCount) { 9 | this.numberOfFeathers = initialFeatherCount; 10 | } 11 | 12 | public void swim() { 13 | this.currentLocation = "in the water"; 14 | } 15 | 16 | public void molt() { 17 | this.numberOfFeathers -= 4; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /open_closed/good/spec/GreeterTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | /** 6 | * Created by mrk on 4/7/14. 7 | */ 8 | class MockPersonality implements Personality { 9 | public String greet() { 10 | return "foo"; 11 | } 12 | } 13 | 14 | public class GreeterTest { 15 | @Test 16 | public void testGreetsSomeone() { 17 | MockPersonality mockPersonality = new MockPersonality(); 18 | Greeter greeter = new Greeter(mockPersonality); 19 | 20 | assertEquals("foo", greeter.greet()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /interface_segregation/good/spec/EagleTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | /** 6 | * Created by mrk on 4/7/14. 7 | */ 8 | public class EagleTest { 9 | @Test 10 | public void testItCanFly() { 11 | Eagle eagle = new Eagle(5); 12 | eagle.fly(); 13 | assertEquals("in the air", eagle.currentLocation); 14 | } 15 | 16 | @Test 17 | public void testItLosesFeathers() { 18 | Eagle eagle = new Eagle(5); 19 | eagle.molt(); 20 | assertEquals(4, eagle.numberOfFeathers); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /interface_segregation/bad/spec/EagleTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | /** 6 | * Created by mrk on 4/7/14. 7 | */ 8 | public class EagleTest { 9 | @Test 10 | public void testItFliesInTheAir() { 11 | Eagle eagle = new Eagle(5); 12 | eagle.fly(); 13 | assertEquals("in the air", eagle.currentLocation); 14 | } 15 | 16 | @Test 17 | public void testItLosesFeathers() { 18 | Eagle eagle = new Eagle(5); 19 | eagle.molt(); 20 | assertEquals(4, eagle.numberOfFeathers); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /interface_segregation/bad/src/Penguin.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/7/14. 3 | */ 4 | public class Penguin implements Bird { 5 | String currentLocation; 6 | int numberOfFeathers; 7 | 8 | public Penguin(int initialFeatherCount) { 9 | this.numberOfFeathers = initialFeatherCount; 10 | } 11 | 12 | public void molt() { 13 | this.numberOfFeathers -= 1; 14 | } 15 | 16 | public void fly() { 17 | throw new UnsupportedOperationException(); 18 | } 19 | 20 | public void swim() { 21 | this.currentLocation = "in the water"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /liskov_substitution/bad/spec/StudioTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | /** 6 | * Created by mrk on 4/8/14. 7 | */ 8 | public class StudioTest { 9 | @Test 10 | public void testInitializedWithZeroBedrooms() { 11 | Studio studio = new Studio(); 12 | assertEquals(0, studio.numberOfBedrooms); 13 | } 14 | 15 | @Test 16 | public void testSetSquareFootage() { 17 | Studio studio = new Studio(); 18 | studio.setSquareFootage(600); 19 | assertEquals(600, studio.squareFootage); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /liskov_substitution/good/spec/StudioTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | /** 6 | * Created by mrk on 4/8/14. 7 | */ 8 | public class StudioTest { 9 | @Test 10 | public void testInitializedWithZeroBedrooms() { 11 | Studio studio = new Studio(); 12 | assertEquals(0, studio.numberOfRooms); 13 | } 14 | 15 | @Test 16 | public void testSetsSquareFootage() { 17 | Studio studio = new Studio(); 18 | studio.setSquareFootage(550); 19 | 20 | assertEquals(550, studio.squareFootage); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /interface_segregation/good/spec/PenguinTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | /** 6 | * Created by mrk on 4/7/14. 7 | */ 8 | public class PenguinTest { 9 | @Test 10 | public void testItCanSwim() { 11 | Penguin penguin = new Penguin(5); 12 | penguin.swim(); 13 | assertEquals("in the water", penguin.currentLocation); 14 | } 15 | 16 | @Test 17 | public void testItLosesFeathersQuickly() { 18 | Penguin penguin = new Penguin(5); 19 | penguin.molt(); 20 | assertEquals(1, penguin.numberOfFeathers); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /liskov_substitution/bad/spec/PenthouseSuiteTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | /** 6 | * Created by mrk on 4/8/14. 7 | */ 8 | public class PenthouseSuiteTest { 9 | @Test 10 | public void testInitializedWithFourBedrooms() { 11 | PenthouseSuite penthouse = new PenthouseSuite(); 12 | assertEquals(4, penthouse.numberOfBedrooms); 13 | } 14 | 15 | @Test 16 | public void testSetsSquareFootage() { 17 | PenthouseSuite penthouse = new PenthouseSuite(); 18 | penthouse.setSquareFootage(1500); 19 | assertEquals(1500, penthouse.squareFootage); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /liskov_substitution/good/spec/PenthouseSuiteTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | /** 6 | * Created by mrk on 4/8/14. 7 | */ 8 | public class PenthouseSuiteTest { 9 | @Test 10 | public void testInitializedWithFourBedrooms() { 11 | PenthouseSuite penthouse = new PenthouseSuite(); 12 | assertEquals(4, penthouse.numberOfBedrooms); 13 | } 14 | 15 | @Test 16 | public void testSetsSquareFootage() { 17 | PenthouseSuite penthouse = new PenthouseSuite(); 18 | penthouse.setSquareFootage(1600); 19 | 20 | assertEquals(1600, penthouse.squareFootage); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /open_closed/bad/src/Greeter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/7/14. 3 | */ 4 | public class Greeter { 5 | String formality; 6 | 7 | public String greet() { 8 | if (this.formality == "formal") { 9 | return "Good evening, sir."; 10 | } 11 | else if (this.formality == "casual") { 12 | return "Sup bro?"; 13 | } 14 | else if (this.formality == "intimate") { 15 | return "Hello Darling!"; 16 | } 17 | else { 18 | return "Hello."; 19 | } 20 | } 21 | 22 | public void setFormality(String formality) { 23 | this.formality = formality; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /dependency_inversion/good/spec/EmailClientTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.PrintStream; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | 8 | /** 9 | * Created by mrk on 4/8/14. 10 | */ 11 | public class EmailClientTest { 12 | private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); 13 | 14 | @Test 15 | public void testAlertsWhenSunny() { 16 | EmailClient gmail = new EmailClient(); 17 | System.setOut(new PrintStream(outContent)); 18 | gmail.alertWeatherConditions("sunny"); 19 | 20 | assertEquals("It is sunny", outContent.toString()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /dependency_inversion/good/spec/MobileDeviceTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.PrintStream; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | 8 | /** 9 | * Created by mrk on 4/8/14. 10 | */ 11 | public class MobileDeviceTest { 12 | private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); 13 | 14 | @Test 15 | public void testAlertsWhenRainy() { 16 | MobileDevice iPhone = new MobileDevice(); 17 | System.setOut(new PrintStream(outContent)); 18 | iPhone.alertWeatherConditions("rainy"); 19 | 20 | assertEquals("It is rainy", outContent.toString()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /single_responsibility/good/spec/BoardPresenterTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.PrintStream; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | 8 | /** 9 | * Created by mrk on 4/7/14. 10 | */ 11 | public class BoardPresenterTest { 12 | private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); 13 | 14 | @Test 15 | public void testBoardPresenterPrintsBoardToConsole() { 16 | Board board = new Board(3); 17 | BoardPresenter presenter = new BoardPresenter(board); 18 | System.setOut(new PrintStream(outContent)); 19 | presenter.displayBoard(); 20 | 21 | assertEquals("0|1|2\n3|4|5\n6|7|8\n", outContent.toString()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /single_responsibility/good/src/BoardShaper.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | 3 | /** 4 | * Created by mrk on 4/7/14. 5 | */ 6 | public class BoardShaper { 7 | int size; 8 | 9 | public BoardShaper(int size) { 10 | this.size = size; 11 | } 12 | 13 | 14 | public ArrayList> rowIndexes() { 15 | ArrayList> rowIndexes = new ArrayList>(); 16 | 17 | for (int i = 0; i < this.size; i++) { 18 | ArrayList row = new ArrayList(); 19 | for (int j = 0; j < this.size; j++) { 20 | row.add((i*size)+(j)); 21 | } 22 | rowIndexes.add(row); 23 | } 24 | 25 | return rowIndexes; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /single_responsibility/good/src/BoardPresenter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/7/14. 3 | */ 4 | public class BoardPresenter { 5 | Board board; 6 | 7 | public BoardPresenter(Board board) { 8 | this.board = board; 9 | } 10 | 11 | public void displayBoard() { 12 | String formattedBoard = ""; 13 | for (int i = 0; i < this.board.size*this.board.size; i++) { 14 | String borderOrNewline = ""; 15 | if ((i+1) % board.size == 0) { 16 | borderOrNewline += "\n"; 17 | } 18 | else { 19 | borderOrNewline += "|"; 20 | } 21 | formattedBoard += board.spots.get(i); 22 | formattedBoard += borderOrNewline; 23 | } 24 | System.out.print(formattedBoard); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /dependency_inversion/bad/src/WeatherTracker.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by mrk on 4/8/14. 3 | */ 4 | public class WeatherTracker { 5 | String currentConditions; 6 | Phone phone; 7 | Emailer emailer; 8 | 9 | public WeatherTracker() { 10 | phone = new Phone(); 11 | emailer = new Emailer(); 12 | } 13 | 14 | public void setCurrentConditions(String weatherDescription) { 15 | this.currentConditions = weatherDescription; 16 | if (weatherDescription == "rainy") { 17 | String alert = phone.generateWeatherAlert(weatherDescription); 18 | System.out.print(alert); 19 | } 20 | if (weatherDescription == "sunny") { 21 | String alert = emailer.generateWeatherAlert(weatherDescription); 22 | System.out.print(alert); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /single_responsibility/good/spec/BoardShaperTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import java.util.ArrayList; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | /** 8 | * Created by mrk on 4/7/14. 9 | */ 10 | public class BoardShaperTest { 11 | @Test 12 | public void testItReturnsRowIndexes() { 13 | BoardShaper shaper = new BoardShaper(2); 14 | ArrayList> rowIndexes = new ArrayList>(); 15 | ArrayList rowOne = new ArrayList(); 16 | ArrayList rowTwo = new ArrayList(); 17 | rowOne.add(0); 18 | rowOne.add(1); 19 | rowTwo.add(2); 20 | rowTwo.add(3); 21 | rowIndexes.add(rowOne); 22 | rowIndexes.add(rowTwo); 23 | 24 | assertEquals(rowIndexes, shaper.rowIndexes()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /single_responsibility/good/src/Board.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | 3 | /** 4 | * Created by mrk on 4/7/14. 5 | */ 6 | public class Board { 7 | int size; 8 | ArrayList spots; 9 | 10 | public Board(int size) { 11 | this.size = size; 12 | this.spots = new ArrayList(); 13 | for (int i = 0; i < size; i++) { 14 | this.spots.add(String.valueOf(3*i)); 15 | this.spots.add(String.valueOf(3*i + 1)); 16 | this.spots.add(String.valueOf(3*i + 2)); 17 | } 18 | } 19 | 20 | public ArrayList valuesAt(ArrayList indexes) { 21 | ArrayList values = new ArrayList(); 22 | 23 | for (int index : indexes) { 24 | values.add(this.spots.get(index)); 25 | } 26 | 27 | return values; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /liskov_substitution/README.md: -------------------------------------------------------------------------------- 1 | # Liskov Substitution Principle 2 | 3 | **Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.** 4 | 5 | The key to understanding the Liskov Substitution Principle is thinking about _processes that use_ (sub)classes, rather than the (sub)classes themselves. In the bad example here, the UnitUpgrader purports to accept any Apartment (an abstract class) and upgrade it. However, once the UnitUpgrader starts upgrading the apartment (`upgrade(Apartment)`), it checks the specific class/subtype of the Apartment object to make sure it doesn't add a bedroom to a Studio (which by definition has zero bedrooms). A Studio object therefore cannot be substituted in for any Apartment. 6 | 7 | If you don't follow the LSP, external processes will either break, behave improperly, or need to know too much information. 8 | -------------------------------------------------------------------------------- /open_closed/README.md: -------------------------------------------------------------------------------- 1 | # Open/Closed Principle 2 | 3 | **Software entitites should be open for extension, but closed for modification** 4 | 5 | I find the Strategy Pattern a great demonstration of the open/closed principle. Notice how in the bad example, any time we want to add a new style of greeting, we have to change the Greeter class to accept a new type of personality. We don't want to have to modify our existing, working code to add something new. Instead, as demonstrated in the good example, we have a high-level Greeter class that is instantiated with some Personality... we don't know which yet, just that it will be some object that implements the Personality interface. Now we can add new objects like FormalPersonality, CasualPersonality, and IntimatePersonality, and just make sure they correctly implement the Personality interface (in this case that means they must have a `greet()` method). The Greeter class is now open for future extension, while remaining closed for modification. 6 | -------------------------------------------------------------------------------- /interface_segregation/bad/spec/PenguinTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Rule; 2 | import org.junit.Test; 3 | import org.junit.rules.ExpectedException; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | /** 8 | * Created by mrk on 4/7/14. 9 | */ 10 | public class PenguinTest { 11 | @Rule 12 | public ExpectedException exception = ExpectedException.none(); 13 | 14 | @Test 15 | public void testItLosesFeathers() { 16 | Penguin penguin = new Penguin(5); 17 | penguin.molt(); 18 | assertEquals(4, penguin.numberOfFeathers); 19 | } 20 | 21 | @Test 22 | public void testItCantActuallyFly() { 23 | Penguin penguin = new Penguin(5); 24 | exception.expect(UnsupportedOperationException.class); 25 | penguin.fly(); 26 | } 27 | 28 | @Test 29 | public void testItCanSwim() { 30 | Penguin penguin = new Penguin(5); 31 | penguin.swim(); 32 | assertEquals("in the water", penguin.currentLocation); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /open_closed/bad/spec/GreeterTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | /** 6 | * Created by mrk on 4/7/14. 7 | */ 8 | public class GreeterTest { 9 | @Test 10 | public void testSaysHello() { 11 | Greeter greeter = new Greeter(); 12 | assertEquals("Hello.", greeter.greet()); 13 | } 14 | 15 | @Test 16 | public void testSaysHelloFormally() { 17 | Greeter greeter = new Greeter(); 18 | greeter.setFormality("formal"); 19 | assertEquals("Good evening, sir.", greeter.greet()); 20 | } 21 | 22 | @Test 23 | public void testSaysHelloCasually() { 24 | Greeter greeter = new Greeter(); 25 | greeter.setFormality("casual"); 26 | assertEquals("Sup bro?", greeter.greet()); 27 | } 28 | 29 | @Test 30 | public void testSaysHelloIntimately() { 31 | Greeter greeter = new Greeter(); 32 | greeter.setFormality("intimate"); 33 | assertEquals("Hello Darling!", greeter.greet()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /dependency_inversion/README.md: -------------------------------------------------------------------------------- 1 | # Dependency Inversion Principle 2 | 3 | **High-level modules should not depend on low-level modules. Both should depend on abstractions.** 4 | 5 | **Abstractions should not depend on details. Details should depend on abstractions.** 6 | 7 | The DIP is concerned with reusability. The high-level modules or interfaces of an application should only be describing the "general flow" of behavior. In some cases this may be considered "business logic". Meanwhile, the low-level modules are written in such a way to apply their concrete details to the abstraction. (The Adapter Pattern is a good example of DIP.) 8 | 9 | In the bad example here, the WeatherTracker depends on the low-level details of the different notification systems (a phone, an emailer, etc.). These should instead be depending on some abstraction. The good example introduces this abstraction--a "Notifier" interface. 10 | 11 | Strongly recommended reading: [Heuristics and Coffee](http://docs.google.com/file/d/0BwhCYaYDn8EgY2I3ZjUzNWMtMzE0ZS00ZDhlLTlmZGItMThkOTQzYzM0MTE3/edit?hl=en) by Uncle Bob 12 | -------------------------------------------------------------------------------- /single_responsibility/good/spec/BoardTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import java.util.ArrayList; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | /** 8 | * Created by mrk on 4/7/14. 9 | */ 10 | public class BoardTest { 11 | @Test 12 | public void testBoardHasASize() { 13 | Board board = new Board(3); 14 | assertEquals(3, board.size); 15 | } 16 | 17 | @Test 18 | public void testBoardSpotCountIsSquareOfSide() { 19 | Board board = new Board(3); 20 | assertEquals(9, board.spots.size()); 21 | } 22 | 23 | @Test 24 | public void testBoardReturnsValuesAtListOfSpots() { 25 | Board board = new Board(3); 26 | 27 | ArrayList values = new ArrayList(); 28 | values.add("0"); 29 | values.add("4"); 30 | values.add("7"); 31 | 32 | ArrayList indexes = new ArrayList(); 33 | indexes.add(0); 34 | indexes.add(4); 35 | indexes.add(7); 36 | 37 | assertEquals(values, board.valuesAt(indexes)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /single_responsibility/README.md: -------------------------------------------------------------------------------- 1 | # Single Responsibility Principle 2 | 3 | **Every class should have a single responsibility. There should never be more than one reason for a class to change.** 4 | 5 | This example is derived from my Tic Tac Toe game. The bad example provides a generic Board class that does board-related things--it stores the values of spots on the board, returns the board's rows, and prints the board out to the screen. This approach reminds me of models in many Rails apps. On the surface, everything seems legitimately related to a real-world Board object, but the Single Responsibility Principle tells us that this class is actually handling far too many responsibilities. 6 | 7 | Consider the Board class in the "good" example. The only thing it is responsible for is knowing the values of its spots. It is entirely unconcerned with how those spots are being manipulated per the rules of Tic Tac Toe (rows, columns, diagonals) or displayed to the user (in a console, on the web, etc.). The BoardShaper and BoardPresenter classes are similarly focused on specific tasks. They are also only passed attributes they need; for example, BoardShaper objects are initialized with only a size (they don't need the whole board). 8 | -------------------------------------------------------------------------------- /dependency_inversion/good/spec/WeatherTrackerTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.PrintStream; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | 8 | /** 9 | * Created by mrk on 4/8/14. 10 | */ 11 | class MockNotifier implements Notifier { 12 | public void alertWeatherConditions(String weatherDescription) { 13 | System.out.print("foo"); 14 | } 15 | } 16 | 17 | public class WeatherTrackerTest { 18 | private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); 19 | 20 | @Test 21 | public void testSetsCurrentWeatherConditions() { 22 | WeatherTracker tracker = new WeatherTracker(); 23 | tracker.setCurrentConditions("cloudy"); 24 | 25 | assertEquals("cloudy", tracker.currentConditions); 26 | } 27 | 28 | @Test 29 | public void testNotifiesWhenWeatherChanges() { 30 | WeatherTracker tracker = new WeatherTracker(); 31 | System.setOut(new PrintStream(outContent)); 32 | tracker.setCurrentConditions("cloudy"); 33 | MockNotifier mockNotifier = new MockNotifier(); 34 | tracker.notify(mockNotifier); 35 | 36 | assertEquals("foo", outContent.toString()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /dependency_inversion/bad/spec/WeatherTrackerTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.PrintStream; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | 8 | /** 9 | * Created by mrk on 4/8/14. 10 | */ 11 | public class WeatherTrackerTest { 12 | private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); 13 | 14 | @Test 15 | public void testReturnsCurrentWeather() { 16 | WeatherTracker tracker = new WeatherTracker(); 17 | tracker.setCurrentConditions("rainy"); 18 | 19 | assertEquals("rainy", tracker.currentConditions); 20 | } 21 | 22 | @Test 23 | public void testAlertsPhoneUsersWhenRaining() { 24 | WeatherTracker tracker = new WeatherTracker(); 25 | System.setOut(new PrintStream(outContent)); 26 | tracker.setCurrentConditions("rainy"); 27 | 28 | assertEquals("It is rainy", outContent.toString()); 29 | } 30 | 31 | @Test 32 | public void testAlertsViaEmailWhenSunny() { 33 | WeatherTracker tracker = new WeatherTracker(); 34 | System.setOut(new PrintStream(outContent)); 35 | tracker.setCurrentConditions("sunny"); 36 | 37 | assertEquals("It is sunny", outContent.toString()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /interface_segregation/README.md: -------------------------------------------------------------------------------- 1 | # Interface Segregation Principle 2 | 3 | **Clients should not be forced to depend on interfaces they do not use.** 4 | 5 | It's easy to get caught in a trap of naming interfaces or abstract classes after real-world things. The problem with this approach is two-fold: the collection of methods defined in the interface increase as one adds more and more functionality of the object to the code (a violation of the Single Responsibility Principle), and implementations of the interface start to require exceptions to the rules of the interface. Consider the bad example here. It may seem reasonable to create a Bird interface that outlines the basic features of birds--they can fly and they can shed their feathers. It works for plenty of birds (like an eagle), but then we want to add penguins to our code. The penguin is technically a bird, but if we set it to implement our Bird interface, we have to throw an exception for the `fly()` method. The penguin should not be forced to depend on an action it cannot perform. 6 | 7 | Instead, make interfaces more abstract. It helps adhere to both the SRP (the interface is only responsible for one particular behavior) and this Interface Segregation Principle because specific objects (like eagles and penguins) only implement the functionality they need. 8 | -------------------------------------------------------------------------------- /liskov_substitution/bad/spec/UnitUpgraderTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | /** 6 | * Created by mrk on 4/8/14. 7 | */ 8 | public class UnitUpgraderTest { 9 | @Test 10 | public void testUpgraderIncreasesSquareFootageOfPenthouse() { 11 | PenthouseSuite penthouse = new PenthouseSuite(); 12 | penthouse.setSquareFootage(1500); 13 | UnitUpgrader upgrader = new UnitUpgrader(); 14 | upgrader.upgrade(penthouse); 15 | 16 | assertEquals(1540, penthouse.squareFootage); 17 | } 18 | 19 | @Test 20 | public void testUpgraderAddsBedroomToApartment() { 21 | PenthouseSuite penthouse = new PenthouseSuite(); 22 | UnitUpgrader upgrader = new UnitUpgrader(); 23 | upgrader.upgrade(penthouse); 24 | 25 | assertEquals(5, penthouse.numberOfBedrooms); 26 | } 27 | 28 | @Test 29 | public void testUpgraderIncreasesSquareFootageOfStudio() { 30 | Studio studio = new Studio(); 31 | studio.setSquareFootage(550); 32 | UnitUpgrader upgrader = new UnitUpgrader(); 33 | upgrader.upgrade(studio); 34 | 35 | assertEquals(590, studio.squareFootage); 36 | } 37 | 38 | @Test 39 | public void testUpgraderDoesntAddBedroomToStudios() { 40 | Studio studio = new Studio(); 41 | UnitUpgrader upgrader = new UnitUpgrader(); 42 | upgrader.upgrade(studio); 43 | 44 | assertEquals(0, studio.numberOfBedrooms); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /single_responsibility/bad/src/Board.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | 3 | /** 4 | * Created by mrk on 4/7/14. 5 | */ 6 | public class Board { 7 | ArrayList spots; 8 | 9 | public Board() { 10 | this.spots = new ArrayList(); 11 | for (int i = 0; i < 9; i++) { 12 | this.spots.add(String.valueOf(i)); 13 | } 14 | } 15 | 16 | public ArrayList firstRow() { 17 | ArrayList firstRow = new ArrayList(); 18 | firstRow.add(this.spots.get(0)); 19 | firstRow.add(this.spots.get(1)); 20 | firstRow.add(this.spots.get(2)); 21 | return firstRow; 22 | } 23 | 24 | public ArrayList secondRow() { 25 | ArrayList secondRow = new ArrayList(); 26 | secondRow.add(this.spots.get(3)); 27 | secondRow.add(this.spots.get(4)); 28 | secondRow.add(this.spots.get(5)); 29 | return secondRow; 30 | } 31 | 32 | public ArrayList thirdRow() { 33 | ArrayList thirdRow = new ArrayList(); 34 | thirdRow.add(this.spots.get(6)); 35 | thirdRow.add(this.spots.get(7)); 36 | thirdRow.add(this.spots.get(8)); 37 | return thirdRow; 38 | } 39 | 40 | public void display() { 41 | String formattedFirstRow = this.spots.get(0) + " | " + this.spots.get(1) + " | " + this.spots.get(2) + "\n" + this.spots.get(3) + " | " + this.spots.get(4) + " | " + this.spots.get(5) + "\n" + this.spots.get(6) + " | " + this.spots.get(7) + " | " + this.spots.get(8); 42 | System.out.print(formattedFirstRow); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /single_responsibility/bad/spec/BoardTest.java: -------------------------------------------------------------------------------- 1 | import org.junit.Test; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.PrintStream; 5 | import java.util.ArrayList; 6 | 7 | import static org.junit.Assert.assertEquals; 8 | 9 | /** 10 | * Created by mrk on 4/7/14. 11 | */ 12 | public class BoardTest { 13 | private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); 14 | 15 | @Test 16 | public void testBoardHasNineSpots() { 17 | Board board = new Board(); 18 | assertEquals(9, board.spots.size()); 19 | } 20 | 21 | @Test 22 | public void testBoardReturnsFirstRow() { 23 | Board board = new Board(); 24 | ArrayList rowOne = new ArrayList(); 25 | rowOne.add("0"); 26 | rowOne.add("1"); 27 | rowOne.add("2"); 28 | 29 | assertEquals(rowOne, board.firstRow()); 30 | } 31 | 32 | @Test 33 | public void testBoardReturnsSecondRow() { 34 | Board board = new Board(); 35 | ArrayList rowTwo = new ArrayList(); 36 | rowTwo.add("3"); 37 | rowTwo.add("4"); 38 | rowTwo.add("5"); 39 | 40 | assertEquals(rowTwo, board.secondRow()); 41 | } 42 | 43 | @Test 44 | public void testBoardReturnsThirdRow() { 45 | Board board = new Board(); 46 | ArrayList rowThree = new ArrayList(); 47 | rowThree.add("6"); 48 | rowThree.add("7"); 49 | rowThree.add("8"); 50 | 51 | assertEquals(rowThree, board.thirdRow()); 52 | } 53 | 54 | @Test 55 | public void testPrintsBoardToConsole() { 56 | Board board = new Board(); 57 | System.setOut(new PrintStream(outContent)); 58 | board.display(); 59 | assertEquals("0 | 1 | 2\n3 | 4 | 5\n6 | 7 | 8", outContent.toString()); 60 | } 61 | } 62 | --------------------------------------------------------------------------------