Chapter 1: How to write great software(Part 2)

So what have we really done in the last part?

We’ve gotten a lot closer to completing step 1 in building great software (Customer Happiness). We've solved Rick's search problem even though we made the app less fragile and it's not going to break so easily now.

New Changes

Rick's Customers ask for more choices, So Rick came up with a new requirement for his app:

  • He wants the Search tool to return all the guitars that match his client’s specs, not just the first one in his inventory.

image.png

Let's change our code to satisfy our customer's change!

  • First, we need to change the search method to return List of objects
      public List search(Guitar searchGuitar){ // C1
          List matchingGuitars = new LinkedList(); // C2  
          for (Iterator it = guitars.iterator(); it.hasNext();){
              Guitar guitar = (Guitar)it.next();
              // Ignore serial number, price since they're unique.
              String builder = searchGuitar.getBuilder();
              if ((builder != null) && (!builder.equals("")) && (!builder.equals(guitar.getBuilder())))
                  continue
              String model = searchGuitar.getModel();
              if ((model != null) && (!model.equals("")) && (!model.equals(guitar.getModel())))
                  continue
              String type = searchGuitar.getType();
              if ((type != null) && (!type.equals("")) && (!type.equals(guitar.getType())))
                  continue
              String backWood = searchGuitar.getBackWood();
              if ((backWood != null) && (!backWood.equals("")) && (!backWood.equals(guitar.getBackWood())))
                  continue
              String topWood = searchGuitar.getTopWood();
              if ((topWood != null) && (!topWood.equals("")) && (!topWood.equals(guitar.getTopWood())))
                  continue
              matchingGuitars.add(guitar); // C3
          }
          return matchingGuitars; // C4
      }
    

Also, don't forget to change our client code (it now receives list not single obj) 😉

public class  FindGuitarTester {
    public static void main(String[] args){
        Guitar whatErinLikes = new Guitar(“”, 0, Builder.FENDER, “Stratocaster”, Type.ELECTRIC, Wood.ALDER, Wood.ALDER);
        List matchingGuitars = inventory.search(whatErinLikes);
    }
}

Now we need to make sure we’ve actually got those requirements handled by our code. Let’s test things out, and see if our app is working as Rick wants it to:

image.png

So now we have just finished the first step let's move to the next step(Customer Happy✅).

Apply OO Principles(Step 2)

Here we are at step 2 where we should take software that works, and make sure the way it's put together actually makes sense and is flexible to accept changes.

Looking for problems

Let's investigate the whole process of the search() method

  1. Client provides their guitar preferences(clients only specify general properties, so they never supply a serial number or a price).
  2. The Search tool looks through Rick's Inventory.
  3. Each Guitar is compared to the client's preferences(all general properties are compared to the client's preferences).
  4. Rick's client is given a list of matching guitars.

So take a minute here to think if there is anything to improve before you continue reading!

Before I tell you what we should fix in our code let's learn some tips that might help you.

3 Important Tips

  • Objects should do only what their names indicate.

    • if an object is name Jet, it should takeOff() and land(), but it shouldn't takeTicket() that's the job of another object and doesn't belong in jet.
  • Each object should represent a single concept.

    • You don’t want objects serving double or triple duty.
    • Avoid a Duck object that represents a real quacking duck, a yellow plastic duck.
  • Unused properties are a dead giveaway.
    • If you’ve got an object that is being used with no-value or null properties often, you’ve probably got an object doing more than one job.
    • If you rarely have values for a certain property, why is that property part of the object?

Problem

You know, Rick’s clients really aren’t providing a Guitar object. I mean, they don’t actually give him a guitar to compare against his inventory.

Look here we don't really care if it's a guitar object or not we do care only about what specific things Rick’s clients are looking for.

image.png

Solution

So what about creating a new class that encapsulate all these specs together in a single unit? Isn't that better than sending an entire Guitar object which makes no sense?

Before moving further I want to explain a word I said briefly.
Encapsulation is about breaking your app into logical parts and then keeping those parts separate. So just like you keep the data in your classes separate from the rest of your app’s behavior, we can keep the generic properties of a guitar separate from the actual Guitar object itself.

Anytime you see duplicate code, look for a place to encapsulate!

So let's describe our solution using the following steps:

  1. We need to create an object called GuitarSpecs that will wrap all Guitar Specs inside it.
  2. We need to remove the duplicate code from the Guitar Object(Methods and Attributes).
  3. We need to reference the GuitarSpecs object for each Guitar.
  4. Update the Inventory class too (make the search tool compiles!).

image.png

image.png

You've learned a lot about writing great software and there's still more to go! let's end step 2 with some techniques we should always consider when we apply OO principles.

  • Flexibility: Use it so that your software can change and grow without constant rework. it keeps your application from being fragile.
  • Encapsulation: Use it to keep the parts of your code that stay the same separation from the parts that change; then it’s really easy to make changes to your code without breaking everything.
  • Functionality: Without it, you’ll never actually make the customer happy. No matter how well-designed your application is, it's the thing that puts a smile on the customer’s face.
  • Design Pattern: It's all about reuse and making sure you’re not trying to solve a problem that someone else has already figured out.

Design once, design twice(Step 3)

Once you’ve taken the first pass over your software and applied some basic OO principles, you’re ready to begin step 3 and take another look for enhancements, and this time make sure your software is not only flexible but easily reused and extended.

Let’s make sure Inventory.java is Really well-designed

As we know the only thing that's constant is change we need to handle the case when Rick calls us back and asks for new changes it should be easy to add new features(make our code reusable, scalable, and extensible).

So in this step, we will focus on turning our classes into a reusable, extensible piece of software.

How easy is it to make this change to Rick's application?

Take a look at the class diagram for Rick’s application, and think about What you would need to do to add support for 12-string guitars?

  1. What properties and methods would you need to add and to what classes?
  2. What code would you need to change to allow Rick’s clients to search for 12-strings?
  3. How many classes did you have to modify to make this change?

Do you think Rick’s application is well-designed right now?

Problem

Let's walk through applying this change together and see how can we achieve this.

  • GuitarSpec Class

    • We need to add a numStrings property.
    • We need to add getNumStrings() method to return how many strings a guitar has. image.png
  • Guitar Class

    • We need to change the constructor of Guitar class since it takes in all the properties in GuitarSpec and creates a GuitarSpec object itself. image.png
  • Inventory Class

    • We need to change addGuitar() Method for sure to accept this new guitar specification.
    • We need to change search() Method to handle this new specification too. image.png

So that's the problem (Lots of modifications to accept only one new change). We Shouldn't have to change the code in Guitar and Inventory to add a new property to the GuitarSpec class.

Solution

As we can see all classes are interdependent. So what about more encapsulation?

Here are some steps we can follow to achieve this:

  • Add a numStrings property and getNumStrings() method to GuitarSpec class.

image.png

  • Modify the Guitar class so that properties of GuitarSpecs are encapsulated away from the constructor of the class

image.png

  • Change the search() method in the Inventory class to delegate comparing the two GuitarSpec objects to the GuitarSpec class, instead of handling it directly.

image.png

Delegation is when an object needs to perform a certain task, and instead of doing that task directly, it asks another object to handle the task (or sometimes just a part of the task).

Congratulations! You’ve turned Rick’s broken inventory search tool into a well-designed piece of great software.

Chapter Recap

Let's recap how we got Rick's search tool working too well(And well designed too 😎).

image.png

References

Head First Object-Oriented Analysis And Design