Design Patterns: Elements of Reusable Object-Oriented Software

Author: Ralph Johnson, Erich Gamma, John Vlissides, Richard Helm
4.5
All Comments
TopTalkedBooks posted at August 19, 2017

This one is a must for any programmer.

Design Patterns: Elements of Reusable Object-Oriented Software https://toptalkedbooks.com/amzn/0201633612

TopTalkedBooks posted at August 19, 2017
TopTalkedBooks posted at August 19, 2017

Regarding a dictionary: not that I know of, no unfortunately.

Also no, programs are not always laid out in the same fashion. There are the concepts of design and architecture. For the purposes here, I'm going to define architecture as how the entire system/program is put together, from the highest-level. Often this involves modules that each contain multiple classes (in OOD). Design, on the other hand, I'll define as how a small number of classes, within those larger modules, are related and interact. This is usually a class diagram in UML. Large scale vs. small scale essentially.

Architecture's goal is reducing risk and ensuring that the application/system exhibit certain non-functional requirements (or quality attributes in SEI-speak). These are '-ilities'. E.g., security, maintainability, modifiability, testability, but also time-to-market, performance, etc. One of the things to remember with architecture is that it encompasses not only the compile-time structure of the code, but also the runtime structure, the deployment (systems-level) structure, and the relationship to other systems (context) and, potentially, time.

While design also influences (and is influenced by) the non-functional requirements, it also concerns itself with data encapsulation: what data should be contained within what objects and how should it be accessed and modified, how tightly coupled and cohesive they should be.

Suggestions for architecture books (these are the two I have personal experience with):
1) Software Architectures in Practice - It's written by the SEI so it's very much a textbook, but I think they do a good job. 2) Designing Software Architectures - Another SEI book

Suggestions for design books:
1) Design Patterns by the Gang of Four - This is the go-to for most people but isn't the easiest book to read or use. One important thing to keep in mind is that this book is a catalog of design patterns the authors came across/found. It's not a book of recommendations. For example: they have the Singleton pattern but most experienced devs abhor that pattern (though every pattern has its uses).
2) Head First Design Patterns - The Head First series has a lot of fans but I've never used this particular book.

TopTalkedBooks posted at August 19, 2017
>, the good old Design Patterns book, responsible for more atrocious over-abstracted,

This is a misunderstanding of the DP book. It would be similar to saying that the existence of TVTropes.com is responsible for terrible scripts of tv shows and movies. Or, the existence of the Oxford English Dictionary is responsible for bad novels and useless documentation.

The DP book is a catalog (to gain awareness) and not a checklist (that you must do). It's a collection of observations about software structures out in the wild. It's not about prescription.

Even without the phrase "design patterns", it's easy to surmise that programmers out there are independently and unknowingly re-inventing the same higher-level structures (similar to tv tropes) but without giving them formal names. The DP book gives them formal names and hence, an attempt at shared vocabulary.

One can go to the amazon.com page for Design Patterns and click "Look inside" for the "First Pages" and see that the authors wanted to present a catalog: https://www.amazon.com/Design-Patterns-Elements-Reusable-Obj...

TopTalkedBooks posted at August 19, 2017
Design Patterns: Elements of Reusable Object-Oriented Software http://www.amazon.com/Design-Patterns-Elements-Reusable-Obje...

Practical Object-Oriented Design in Ruby by Sandi Metz. http://www.poodr.com/

TopTalkedBooks posted at August 19, 2017
The only way to learn how to program is to just do it. Pick a language. Start with small programs. Learn the features and syntax of the language. When you feel you know the language well enough to write a program that uses most of its features, then pick an open source program and learn its code. Make some modifications to it. When you feel you're pretty comfortable in one language, pick a new language.

Rinse repeat. You'll find that once you have one language down, the process will go much faster with the second. The features and syntax of most languages are pretty similar.

You can pick any language you like to start with, but personally I'd recommend starting with a hard typed compiled language rather than a dynamic language. Dynamic languages are easy to learn, but its easy to be a lazy and bad programmer in them. They let you get away with too much. Hard typed compiled languages are a) much better at catching your errors and b) much harder to be a lazy programmer in. They don't let you get away with things nearly as much.

I'd recommend Java, seems to be the language a lot of people start with. It will also give you a good founding in open source programming. I started with C/C++ and I'm glad I did. But that was more of a challenge - had to learn pointers and memory management in my first language. And after that I took a little while to learn the open source mindset, since I'd started in a procedural language.

Once you have a good grasp of an object oriented language like Java (or Python) and what object oriented programming means, then I'd recommend reading the Gang of Four book on Design Patterns (http://www.amazon.com/Design-Patterns-Elements-Reusable-Obje...) and Martin Fowler's book on Refactoring (http://www.amazon.com/Refactoring-Improving-Design-Existing-...). Those will give you a pretty good basis in software design and maintenance.

In terms of recommended reading for learning a language, the O'Reilly books are nearly always good in my experience. Here's one for Java (Learning Java: http://www.amazon.com/Learning-Java-Patrick-Niemeyer/dp/0596...) and here's one for Python (Learning Python: http://www.amazon.com/Learning-Python-Powerful-Object-Orient...)

TopTalkedBooks posted at August 20, 2017

Store a reference to the parent and make the Level property recursive.

I added an example and a couple other design suggestions in the code sample below. Hope this helps. FYI, this is pretty much straight out of the Gang of Four's design for the Composite Pattern, which should be required reading for anyone who is serious about OOP.

  • DoFactory .NET Composite Pattern
  • Design Patterns: Elements of Reusable Object-Oriented Software, on Amazon.com

        public class Foo
        {
    
            public Foo(Foo parent = default(Foo))
            { 
                this.parent = parent;
                this.children = new List<Foo>();
            }
    
            private readonly Foo parent;
            private readonly List<Foo> children;
    
            public int Level { get { return ReferenceEquals(parent,null) ? 0 : parent.Level + 1; } }
    
            // don't expose the actual list... see below for why
            public IEnumerable<Foo> Children { get { foreach(Foo child in this.children) yield return child; } }
    
            //  instead of exposing the child object list
            //  declare an explicit method with any parameters 
            //  necessary.  this allows you to enforce the invariant 
            //  condition that all objects in a children collection
            //  will have their parent reference set to their 
            //  actual parent
            public void AddChild()
            {
                Foo newChild = new Foo(parent:this);
                this.children.Add(newChild);
            }
    
            //  if you need the ability to remove items as well, 
            //  you can expose a remove method too.  Just make 
            //  sure that you validate expected preconditions
            public int RemoveChild(Foo childToRemove)
            { 
                if(ReferenceEquals(childToRemove,null)) throw new ArgumentNullException("childToRemove");
                if(!ReferenceEquals(this,childToRemove.parent)) throw new ArgumentException("The object cannot be removed because the current object is not the correct parent.","childToRemove"); 
                return children.RemoveAll((Foo existentChild) => existentChild.Equals(childToRemove));
            }
    
    
        }
    
TopTalkedBooks posted at August 20, 2017

Here comes the answer from the GOF:

Coplien describes how to implement functors, objects that are functions, in C++ [Cop92]. He achieves a degree of transparency in their use by overloading the function call operator (operator()). The Command pattern is different; its focus is on maintaining a binding between a receiver and a function (i.e., action), not just maintaining a function.

TopTalkedBooks posted at August 20, 2017

Next should be learning the STL library, and design patterns

TopTalkedBooks posted at August 20, 2017

Heres's a little additional information that may help better understand some of the other technically correct, but shorter answers.

In the strictest sense a Class Factory is a function or method that creates or selects a class and returns it, based on some condition determined from input parameters or global context. This is required when the type of object needed can't be determined until runtime. Implementation can be done directly when classes are themselves objects in the language being used, such as Python.

Since the primary use of any class is to create instances of itself, in languages such as C++ where classes are not objects that can be passed around and manipulated, a similar result can often be achieved by simulating "virtual constructors", where you call a base-class constructor but get back an instance of some derived class. This must be simulated because constructors can't really be virtual in C++, which is why such object—not class—factories are usually implemented as standalone functions or static methods.

Virtual functions are normally resolved "late" by the actual type of object referenced, but in the case of constructors, the object doesn't exist yet, so the type must be determined by some other means.

The best implementations are those that handle new candidate classes automatically when they are added rather than having only a certain finite set currently hardcoded into the factory (although the trade-off is often acceptable if the factory is the only place requiring modification).

James Coplien's 1991 book Advanced C++: Programming Styles and Idioms has details on one way to implement such virtual generic constructors in C++. There are even better ways to do this using C++ templates, but that was not covered in the book which predates their being added to the standard language definition. In fact, C++ templates themselves are class factories since they instantiate a new class whenever they're used with different actual type argument(s). Update: I located a 1998 paper he wrote for EuroPLoP '98 titled C++ Idioms where, among other things, he revises and regroups the idioms in his book into design-pattern form à la the 1994 Design Patterns: Elements of Re-Usable Object-Oriented Software book.

See also the related answers here for the question Class factory in Python. Also see Abstract Factory, Template Style which is a 2001 Dr. Dobb's article also about implementing them with C++ Templates.

TopTalkedBooks posted at August 20, 2017

0. TL;DR

For the impatient coder, a working version of the following implementation can be found on GitHub. This is the same answer written on another Stack Overflow post.

After rewriting the login activity code several times in many different apps, the easy (and not so elegant) solution was create the Google API client as a Application class object. But, since the connection state affect the UX flow, I never was happy about with this approach.

Reducing our problem only to the connection concept, we may consider that:

  1. It hides the Google API client.
  2. It has finite states.
  3. It is a (rather) unique.
  4. The current state affect the behavior of the app.

1. Proxy Pattern

Since the Connection encapsulates the GoogleApiClient, it will implement the ConnectionCallbacks and OnConnectionFailedListener:

@Override
public void onConnected(Bundle hint) {
    changeState(State.OPENED);
}

@Override
public void onConnectionSuspended(int cause) {
    changeState(State.CLOSED);
    connect();
}

@Override
public void onConnectionFailed(ConnectionResult result) {
    if (currentState.equals(State.CLOSED) && result.hasResolution()) {
        changeState(State.CREATED);
        connectionResult = result;
    } else {
        connect();
    }
}

Activities can communicate to the Connection class through the methods connect, disconnect, and revoke, but their behaviors are decided by the current state. The following methods are required by the state machine:

protected void onSignIn() {
    if (!googleApiClient.isConnected() && !googleApiClient.isConnecting()) {
        googleApiClient.connect();
    }
}

protected void onSignOut() {
    if (googleApiClient.isConnected()) {
        Plus.AccountApi.clearDefaultAccount(googleApiClient);
        googleApiClient.disconnect();
        googleApiClient.connect();
        changeState(State.CLOSED);
    }
}

protected void onSignUp() {
    Activity activity = activityWeakReference.get();
    try {
        changeState(State.OPENING);
        connectionResult.startResolutionForResult(activity, REQUEST_CODE);
    } catch (IntentSender.SendIntentException e) {
        changeState(State.CREATED);
        googleApiClient.connect();
    }
}

protected void onRevoke() {
    Plus.AccountApi.clearDefaultAccount(googleApiClient);
    Plus.AccountApi.revokeAccessAndDisconnect(googleApiClient);
    googleApiClient = googleApiClientBuilder.build();
    googleApiClient.connect();
    changeState(State.CLOSED);
}

2. State Pattern

This is a behavioral pattern the allow an object to alter its behavior when its internal state changes. The GoF Design Patterns book describes how a TCP connection can be represent by this pattern (which is also our case).

A state from a state machine should be a singleton, and the easiest away of doing it in Java was to create Enum named State as follows:

public enum State {
    CREATED {
        @Override
        void connect(Connection connection) {
            connection.onSignUp();
        }
        @Override
        void disconnect(Connection connection) {
            connection.onSignOut();
        }
    },
    OPENING {},
    OPENED {
        @Override
        void disconnect(Connection connection) {
            connection.onSignOut();
        }
        @Override
        void revoke(Connection connection) {
            connection.onRevoke();
        }
    },
    CLOSED {
        @Override
        void connect(Connection connection) {
            connection.onSignIn();
        }
    };

void connect(Connection connection) {}
void disconnect(Connection connection) {}
void revoke(Connection connection) {}

The Connection class holds the context, i.e. the current state, which defines how the Connection methods connect, disconnect, and revoke will behave:

public void connect() {
    currentState.connect(this);
}

public void disconnect() {
    currentState.disconnect(this);
}

public void revoke() {
    currentState.revoke(this);
}

private void changeState(State state) {
    currentState = state;
    setChanged();
    notifyObservers(state);
}

3. Singleton Pattern

Since there is not need to recreate this class repeatedly, we provide it as a singleton:

public static Connection getInstance(Activity activity) {
    if (null == sConnection) {
        sConnection = new Connection(activity);
    }

    return sConnection;
}

public void onActivityResult(int result) {
    if (result == Activity.RESULT_OK) {
        changeState(State.CREATED);
    } else {
        changeState(State.CLOSED);
    }
    onSignIn();
}

private Connection(Activity activity) {
    activityWeakReference = new WeakReference<>(activity);

    googleApiClientBuilder = new GoogleApiClient
           .Builder(activity)
           .addConnectionCallbacks(this)
           .addOnConnectionFailedListener(this)
           .addApi(Plus.API, Plus.PlusOptions.builder().build())
           .addScope(new Scope("email"));

    googleApiClient = googleApiClientBuilder.build();
    currentState = State.CLOSED;
}

4. Observable Pattern

The Connection class extends Java Observable, so 1 or more activities can observe the state changes:

@Override
protected void onCreate(Bundle bundle) {
    connection = Connection.getInstance(this);
    connection.addObserver(this);
}

@Override
protected void onStart() {
    connection.connect();
}

@Override
protected void onDestroy() {
    connection.deleteObserver(this);
    connection.disconnect();
}

@Override
protected void onActivityResult(int request, int result, Intent data) {
    if (Connection.REQUEST_CODE == request) {
        connection.onActivityResult(result);
    }
}

@Override
public void update(Observable observable, Object data) {
    if (observable != connection) {
        return;
    }
    // Your presentation logic goes here...
}
TopTalkedBooks posted at August 20, 2017

The Decorator Pattern is probably the most straight forward one to use and would be a good one to extend concrete objects functionality and/or characteristics.

Here is some light reading: Head First Design Patterns - CH3 pdf

FYI, couple must have's for learning and referencing design patterns regardless your language of choice:

1) Head First Design Patterns

2) Patterns for Enterprise Application Architecture

3) Design Patterns: Elements of Reusable Object-Oriented Software

And sites:

1) DoFactory

2) StackOverflow Design Patterns Newbie

There are a few others, I'll have to dig them up.

TopTalkedBooks posted at August 20, 2017

This is actually simple to do once you understand that DI is about patterns and principles, not technology.

To design the API in a DI Container-agnostic way, follow these general principles:

Program to an interface, not an implementation

This principle is actually a quote (from memory though) from Design Patterns, but it should always be your real goal. DI is just a means to achieve that end.

Apply the Hollywood Principle

The Hollywood Principle in DI terms says: Don't call the DI Container, it'll call you.

Never directly ask for a dependency by calling a container from within your code. Ask for it implicitly by using Constructor Injection.

Use Constructor Injection

When you need a dependency, ask for it statically through the constructor:

public class Service : IService
{
    private readonly ISomeDependency dep;

    public Service(ISomeDependency dep)
    {
        if (dep == null)
        {
            throw new ArgumentNullException("dep");
        }

        this.dep = dep;
    }

    public ISomeDependency Dependency
    {
        get { return this.dep; }
    }
}

Notice how the Service class guarantees its invariants. Once an instance is created, the dependency is guaranteed to be available because of the combination of the Guard Clause and the readonly keyword.

Use Abstract Factory if you need a short-lived object

Dependencies injected with Constructor Injection tend to be long-lived, but sometimes you need a short-lived object, or to construct the dependency based on a value known only at run-time.

See this for more information.

Compose only at the Last Responsible Moment

Keep objects decoupled until the very end. Normally, you can wait and wire everything up in the application's entry point. This is called the Composition Root.

More details here:

Simplify using a Facade

If you feel that the resulting API becomes too complex for novice users, you can always provide a few Facade classes that encapsulate common dependency combinations.

To provide a flexible Facade with a high degree of discoverability, you could consider providing Fluent Builders. Something like this:

public class MyFacade
{
    private IMyDependency dep;

    public MyFacade()
    {
        this.dep = new DefaultDependency();
    }

    public MyFacade WithDependency(IMyDependency dependency)
    {
        this.dep = dependency;
        return this;
    }

    public Foo CreateFoo()
    {
        return new Foo(this.dep);
    }
}

This would allow a user to create a default Foo by writing

var foo = new MyFacade().CreateFoo();

It would, however, be very discoverable that it's possible to supply a custom dependency, and you could write

var foo = new MyFacade().WithDependency(new CustomDependency()).CreateFoo();

If you imagine that the MyFacade class encapsulates a lot of different dependencies, I hope it's clear how it would provide proper defaults while still making extensibility discoverable.


FWIW, long after writing this answer, I expanded upon the concepts herein and wrote a longer blog post about DI-Friendly Libraries, and a companion post about DI-Friendly Frameworks.

TopTalkedBooks posted at August 20, 2017

If you are looking for physical validation, what I usually find that helps is doing some prototyping. This gives you a good idea usually of any unforeseen problems that might be in your design and just how easy it is to add onto it. I would try to apply any design patterns possible to allow future scalability. Elements of Reusable Object-Oriented Software is a great reference for that. Here are some good examples that show before and after code using design patterns. This can help you visualize how design patterns could make your code more scalable as well. Here is an SO post about specific design patterns for software scalability.

TopTalkedBooks posted at August 20, 2017

Three things I'd suggest:

Design patterns - Elements of reusable object oriented software - THE original design patterns book. Has examples in C++.

Head first design patterns - a very nice books detailing design patterns in Java. An enjoyable view.

Real world examples of design patterns in JDK - a terrific Stack overflow answer by BalusC.

TopTalkedBooks posted at August 20, 2017

Can somebody please explain in layman's terms?

Design patterns are not really "layman" concepts, but I'll try to make it as clear as possible. Any design pattern can be considered in three dimensions:

  1. The problem the pattern solves;
  2. The static structure of the pattern (class diagram);
  3. The dynamics of the pattern (sequence diagrams).

Let's compare State and Strategy.

Problem the pattern solves

State is used in one of two cases [GoF book p. 306]:

  • An object's behavior depends on its state, and it must change its behavior at run-time depending on that state.
  • Operations have large, multipart conditional statements that depend on the object's state. This state is usually represented by one or more enumerated constants. Often, several operations will contain this same conditional structure. The State pattern puts each branch of the conditional in a separate class. This lets you treat the object's state as an object in its own right that can vary independently from other objects.

If you want to make sure you indeed have the problem the State pattern solves, you should be able to model the states of the object using a finite state machine. You can find an applied example here.

Each state transition is a method in the State interface. This implies that for a design, you have to be pretty certain about state transitions before you apply this pattern. Otherwise, if you add or remove transitions, it will require changing the interface and all the classes that implement it.

I personally haven't found this pattern that useful. You can always implement finite state machines using a lookup table (it's not an OO way, but it works pretty well).

Strategy is used for the following [GoF book p. 316]:

  • many related classes differ only in their behavior. Strategies provide a way to configure a class with one of many behaviors.
  • you need different variants of an algorithm. For example, you might define algorithms reflecting different space/time trade-offs. Strategies can be used when these variants are implemented as a class hierarchy of algorithms [HO87].
  • an algorithm uses data that clients shouldn't know about. Use the Strategy pattern to avoid exposing complex, algorithm-specific data structures.
  • a class defines many behaviors, and these appear as multiple conditional statements in its operations. Instead of many conditionals, move related conditional branches into their own Strategy class.

The last case of where to apply Strategy is related to a refactoring known as Replace conditional with polymorphism.

Summary: State and Strategy solve very different problems. If your problem can't be modeled with a finite state machine, then likely State pattern isn't appropriate. If your problem isn't about encapsulating variants of a complex algorithm, then Strategy doesn't apply.

Static structure of the pattern

State has the following UML class structure:

PlantUML class diagram of State Pattern

Strategy has the following UML class structure:

PlantUML class diagram of Strategy Pattern

Summary: in terms of the static structure, these two patterns are mostly identical. In fact, pattern-detecting tools such as this one consider that "the structure of the [...] patterns is identical, prohibiting their distinction by an automatic process (e.g., without referring to conceptual information)."

There can be a major difference, however, if ConcreteStates decide themselves the state transitions (see the "might determine" associations in the diagram above). This results in coupling between concrete states. For example (see the next section), state A determines the transition to state B. If the Context class decides the transition to the next concrete state, these dependencies go away.

Dynamics of the pattern

As mentioned in the Problem section above, State implies that behavior changes at run-time depending on some state of an object. Therefore, the notion of state transitioning applies, as discussed with the relation of the finite state machine. [GoF] mentions that transitions can either be defined in the ConcreteState subclasses, or in a centralized location (such as a table-based location).

Let's assume a simple finite state machine:

PlantUML state transition diagram with two states and one transition

Assuming the subclasses decide the state transition (by returning the next state object), the dynamic looks something like this:

PlantUML sequence diagram for state transitions

To show the dynamics of Strategy, it's useful to borrow a real example.

PlantUML sequence diagram for strategy transitions

Summary: Each pattern uses a polymorphic call to do something depending on the context. In the State pattern, the polymorphic call (transition) often causes a change in the next state. In the Strategy pattern, the polymorphic call does not typically change the context (e.g., paying by credit card once doesn't imply you'll pay by PayPal the next time). Again, the State pattern's dynamics are determined by its corresponding fininte state machine, which (to me) is essential to correct application of this pattern.

TopTalkedBooks posted at August 20, 2017

Head First Design Patterns

alt text

and the Design Pattern Wikipedia page are the best resources for beginners. FluffyCat is another good, free online resource for design patterns in both Java and PHP.

The Gang of Four book is where to go afterward, but it's fairly advanced, so I'd wait until you have a pretty firm grasp from the other resources.

TopTalkedBooks posted at August 20, 2017

You have to make up your mind whether the so called apple-specific method (in this case checkPrice()) is really specific to Apple. Or it is actually generally applicable to all fruits.

A method that is generally applicable should be declared in the base class

Assuming the answer is yes (in this case it does seems to be yes), then you should declare the method in the base class. In this case you can iterate through all the different types of fruits, and all of them would accept the method checkPrice(), so you don't even need to make a special case for apples.

A method that isn't generally applicable can be declared in an interface

What if the answer is no? Let's assume we need another method called getJuicePrice(), and we further assume that only some fruits can be made into juice (apple juice, orange juice) but other cannot (pineapple? durian?). In this case, a simple solution is to declare an interface, and only the fruits for which the method is appropriate would implement the interface. So let's say this interface is JuiceBehavior

package fruitcart;

import java.math.BigDecimal;

public interface JuiceBehavior {
    BigDecimal getJuicePrice();
}

And all fruits for which juice behavior is applicable (yes for Apple, no for Durian) would implement the interface:

package fruitcart;

import java.math.BigDecimal;

public class Apple implements JuiceBehavior {

    @Override
    public BigDecimal getJuicePrice() {
        // FIXME implement this
        return null;
    }

}

And then in your loop, what you check is whether a fruit is instanceof the interface:

if (fruit instanceof JuiceBehavior) {
    System.out.format("can be made into juice "
        + "with price $ %.2f%n", fruit.getJuicePrice());
} else {
    System.out.format("cannot be made into juice %n");

}

This solution would work for simple cases, but in more complicated cases, you may notice that you start to duplicate a lot of implementation code for getJuicePrice() for different types of fruits. This leads to the next topic

Design Pattern: Strategy

You may want to start thinking about the Design Pattern called Strategy, which further encapsulates JuiceBehavior and make it into a family of classes representing different juice behaviors. It also let you set different types of fruits to take different implementations of JuiceBehavior. I won't go into the details here. But you can read up on that on some books about Design Patterns. Such as

  1. Design Patterns: Elements of Reusable Object-Oriented Software
  2. Head First Design Patterns: A Brain-Friendly Guide
TopTalkedBooks posted at August 20, 2017

No, that does not violate any OOP principle.

A prominent example is an object who's behavior depends on whether a connection is established or not (e.g. function doNetworkStuff() depends on openConnection()).

In Java, there is even a typestate checker, which performs such checks (whether Duck can already Quack()) at compile time. I often have such dependencies as preconditions for interfaces, and use a forwarding class whose sole purpose is protocolling and checking the state of the object it forwards to, i.e. protocol which functions have been called on the object, and throw exceptions (e.g. InvalidStateException) when the preconditions are not met.

A design pattern that handles this is state: It allows an object to alter its behavior when its internal state changes. The object will appear to change its class. The design pattern book from the Gang of Four also uses the example above of a network connection either being established or not.

TopTalkedBooks posted at August 20, 2017
The classical Design Patterns book has a first chapter which takes you through the design of a text editor using the patterns provided in the book. If what you do is read the chapter and then the patterns referenced as you go and build the text editor based on their design you get exactly the sort of thing you are looking for. Its a different way of doing it than the entire book but arguably just in a different format for what is otherwise a reference book.

https://www.amazon.com/Design-Patterns-Elements-Reusable-Obj...

TopTalkedBooks posted at August 20, 2017
What i listed was just an example but you can verify quickly - https://www.amazon.com/dp/0201633612/?tag=devbookscom20-20 without opening the actual book.

And afaict - none of the patterns I listed are mentioned verbatim.

TopTalkedBooks posted at November 26, 2017

Re, "Is there some design pattern involved in...?" It is a design pattern. A design pattern is any way of doing something that is replicated by lots of developers. The purpose of the original Design Patterns book was to teach us to give them names so that we may more easily talk about them. I don't know if there's a better name, but most developers would know what I was talking about if I said, "...create a thread with a Runnable delegate."

TopTalkedBooks posted at March 18, 2018

Big List of Resources:

Legend:

  • ¶ Link to a PDF file
  • $ Link to a printed book
TopTalkedBooks posted at March 18, 2018

It is good that you have started organizing your code into objects, this is a good move into the better application structure. Once when you start looking deeper into it, you will find ways to split your current objects into even smaller parts and organize them in better ways, having less code solving more problems in more flexible ways.

For example, in your code the business logic is still tightly coupled to the database. What if you decide to use mysqli instead of PDO? You'll have to touch every class in your application.

But if the database interaction was extracted into own set of objects that were used by your business logic, it would be much easier to replace the database access layer. In fact, you could quite easily replace MySQL with PostgreSQL or even with plain files in that case.

I can think of two ways to learn more about how OOP works: read a book or learn from the existing code.

The book I linked is my favorite OOP book and shows some very good examples of how the problem can be solved with OOP by decomposing the program into the co-operating objects.

And I'd also recommend starting using some OOP framework, I had some good experience with Yii in the past, check the guide to see how it looks like. You'll see tons of useful objects solving various problems you have to solve all the time when developing a web application. Try to build some simple application with it and then try to look inside the framework code to see how it actually works.

One more advice is to look into automatic testing. This will not only keep your application alive, but will teach you how to compose better objects. You'll have to use your classes in two different situations - your actual code and tests. Inside tests you will want to isolate the object you are testing from the rest of the code, for example, test the sales stats algorithms without touching the database. And you'll have to split you code into smaller and more flexible structure to be able to do that.

TopTalkedBooks posted at March 18, 2018

kicks chair back to glance at shelf


Electronics:

The Art of Electronics, 3rd edition (Horwitz/Hill)

Learning the Art of Electronics (Hayes/Horowitz)

*Signal Integrity - Simplified (Bogatin)

Debugging - 9 Indispensible Rules... (Agans)

Software:

The C Programming Language (Kernighan / Ritchie)

The Practice of Programming (Kernighan / Pike)

Clean Code: A Handbook of Agile Software Craftmanship (Martin)

Design Patterns: Elements of Reusable Object-Oriented Software (Gamma, Helm, et al)

Computers:

*Computer Architecture, Fifth Edition: A Quantitative Approach (Hennessy / Patterson)


Note the repeated author names. :) Some of these might be a little beyond you since you're a freshman. I put a star in front of the ones that you might want to wait on until you've got more fundamentals built up. You don't have to wait for your school to teach them to you though.

A great way to jump-start your knowledge is to buy an Arduino experimentation kit (like Adafruit's ARDX) and learn how to use it. You'll get to play with where the code meets the hardware and you'll learn enough electronics knowledge to go all sorts of different directions.

Edit: almost forgot; teach yourself to draw like a mechanical engineer. There's likely an into to mechanical drawings course for the frosh Mechos, take it if you can. It comes up more often than you'd think and it'll help you learn to effectively communicate ideas visually.

TopTalkedBooks posted at March 18, 2018

Not some book, but the book:

https://toptalkedbooks.com/amzn/0201633612

Again, not saying you need to read it cover-to-cover, but if you didn't even know it existed then that's likely a problem.

TopTalkedBooks posted at March 18, 2018

Design Patterns: Elements of Reusable Object-Oriented Software doesn't use JS for it's examples, but is highly regarded in learning design patterns.

Also, Mastering JavaScript Design Patterns is pretty good, and if I recall correctly, is modeled after the first book I mentioned. Heads up, there is a more up to date 2nd edition of this book available (linked version is 1st edition)

Top Books
We collected top books from hacker news, stack overflow, Reddit, which are recommended by amazing people.