Clean Code

Any fool can write code that a computer can understand. Good programmers write code that humans can understand

- Martin Fowler

Leave the campground cleaner than you found it.

Bad Code

We all know what is bad code.

So then - why did we write it?

Were we trying to go fast?

Were we in a rush?

Probably so.

Bad Code

We’ve all said we’d go back and clean it up later

Of course, in those days we didn’t know LeBlanc’s law:

Later equals never.

The only valid measurement of code quality: WTFs/minute.

The Total Cost of Owning a Mess

  • No change is trivial.

In some cases it can lead to zero productivity

  • As the mess builds, the productivity of the team continues to decrease.

The Grand Redesign in the Sky

They inform management that they cannot continue to develop in this odious code base.

Eventually the team rebels.

They demand a redesign.

Why does this happen to code?

We are unprofessional.

How could this mess be our fault?

What about the requirements?

What about the schedule?

What about the managers?

Don’t they bear some of the blame?

No. 

It’s your job to defend the code with equal passion!

The managers look to us for the information they need to make promises and commitments.

We should not be shy about telling them what we think.

Most managers want the truth, even when they don't act like it.

it's not ok

What Is Clean Code?

What Is Clean Code?

Code that anyone can understand with the lowest effort possible.

But here is selection of some phrases from quotes of some very well-known and deeply experienced programmers.

There are probably as many definitions as there are programmers. 

Michael Feathers, author of Working
Effectively with Legacy Code

[...] Clean code always
looks like it was written by someone who cares.
There is nothing obvious that you can do to
make it better. [...]

[...] It has meaningful
names. It provides one way rather than many
ways for doing one thing. It has minimal depen-dencies, [...]

“Big” Dave Thomas, founder
of OTI, godfather of the
Eclipse strategy

[...] Runs all the tests; Contains no duplication; Expresses all the design ideas that are in the system; [...]

Ron Jeffries, author of Extreme Programming
Installed and Extreme Programming
Adventures in C#

Meaningful Names

A Simple Example

function getThem()
{
    $list = [];
    
    foreach($theList as $x)
        if($x[0] === 4)
            $list []= $x;
    
    return $list;
}

There are no complex expressions.

There aren’t even any fancy classes or polymorphic methods

Why is it hard to tell what this code is doing?

A Simple Example

function getFlaggedCells()
{
    $flaggedCells = [];
    
    foreach($gameBoard as $cell)
        if($cell[STATUS_ID] === STATUS_FLAGGED)
            $flaggedCells []= $cell;
    
    return $flaggedCells;
}

Notice that the simplicity of the code has not changed.

It still has exactly the same number of operators/constants/nesting levels.

But the code has become much more explicit.

A Simple Example

function getFlaggedCells()
{
    $flaggedCells = new CellCollection();
    
    foreach($gameBoard as $cell)
        if($cell->isFlagged())
            $flaggedCells->add($cell);
    
    return $flaggedCells;
}

This looks much better.

Use Intention-Revealing Names

If a name requires a comment, then the name does not reveal its intent. 

$val = array();

foreach ($valKey as $k)
{
    $val[] = $values[$k];
}

$val2 = array();

foreach ($valKey2 as $k2)
{
    $val2[] = $values[$k2];
}

Avoid Disinformation

Beware of using names which vary in small ways

$previousStatusData[0]['oldValue'];
$previousResultData[0]['oldValue'];
$previousStatusData[0]['newValue'];
$previousResultData[0]['newValue'];

- Noise words are redundant. 

The word variable should never appear in a variable name. The word table should never appear in a table name.

Make Meaningful Distinctions

- Number-series naming. 

(a1, a2, etc...) Such names are not disinformative—they are noninformative.

- Noise words are another meaningless distinction.

ProductClass, ProductInfo, ProductData.

Use Pronounceable Names

Use Searchable Names

The length of a name should correspond to the size of its scope

Single-letter names and numeric constants have a particular problem in that they are not easy to locate across a body of text.

Avoid Encodings

Interfaces and Implementations

IShapeFactory and ShapeFactory? I don’t want my users knowing that I’m handing them an interface. I just want them to know that it’s a ShapeFactory. 

Hungarian Notation

Member Prefixes

Class Names

Classes and objects should have noun or noun phrase names like Customer, WikiPage, Account, and AddressParser.

Avoid words like Manager, Processor, Data, or Info in the name of a class.

A class name should not be a verb.

Method Names

Methods should have verb or verb phrase names like postPayment, deletePage, or save.

Accessors, mutators, and predicates should be named for their value and prefixed with getset or is

Don’t Be Cute

Method name HolyHandGrenade is cute, but maybe in this case DeleteItems might be a better name.

Choose clarity over entertainment value.

Pick One Word per Concept  

It’s confusing to have fetch, retrieve, get.

Use Solution Domain Names

Remember that the people who read your code will be programmers. 

Add Meaningful Context

There are a few names which are meaningful in and of themselves—most are not.

You can add context by using prefixes:

addrFirstName, addrLastName, addrState, and so on.

A better solution is to create a class named Address.

Prefixing the name should be a last resort.

Don’t Add Gratuitous Context

Shorter names are generally better than longer ones, so long as they are clear. Add no more context to a name than is necessary.

Functions

The second rule of functions is that they should be smaller than that. 

The first rule of functions is that they should be small.

Blocks and Indenting.

This implies that the blocks within if statements, else statements, while statements, and so on should be one line long.

function deleteUser( user )
{
    var date = new Date();
    var freshTime = date.getTime() - 1000 * 60 * 60 * 24 * 30;
    var contracts = getUserContracts(user);
    
    if(user.lastLogin <  freshTime
        && contracts.length === 0)
    {
        var devices = getUserDevices(user);
        for(device : devices)
        {
            device.deleted = 1;
        }
        user.deleted = 1;
    }else
    {
        var validationMsg = "We are sorry but user: " +user.name+
                            " can not be deletet.";
        var logService = getLog();
        logService.warn(validationMsg);
    }
}

Blocks and Indenting.

function checkAndDeleteUser( user )
{
    if ( validForDelete( user ) )
    {
        deleteUser( user );
    } else
    {
        invalidUser( user );
    }
}

Blocks and Indenting.

The following advice has appeared in one form or another for 30 years or more.

FUNCTIONS SHOULD DO ONE THING.

THEY SHOULD DO IT WELL.

THEY SHOULD DO IT ONLY.

The problem with this statement is that it is hard to know what “one thing” is.

Do One Thing

function isEdible(){
    if(this.ExpirationData > Date.Now
    && this.ApprovedForConsumption === true
    && this.InspectorId !== null){
        return true;
    }else{
        return false;
    }
}

Do One Thing

How many things this function is doing?

  1. Checks expiration date
  2. Checks approval
  3. Checks inspector id
  4. Gives answer on request
function isEdible(){
    retun isFresh() && 
          isApproved() && 
          isInspected;
}

Do One Thing

Now it's one thing.

Now this function have only one reason to change.

That is the same function

I have spend about 5-10 minutes.

And I not only deleted code and replaced it by function calls.

I actually refactored whole thing.

One Level of Abstraction per Function

In order to make sure our functions are doing “one thing,” we need to make sure that the statements within our function are all at the same level of abstraction

Mixing levels of abstraction within a function is always confusing.

function doHousework()
{
    cleanTheFloor();
    pourFlowers();
    for(Dog dog : dogsStack)
    {
        if(dog.hungry())
        {
            prepareFood();
            feed(dog);
        }
    }
}
function doHousework()
{
    cleanTheFloor();
    pourFlowers();
    feedDogs();
}

By their nature, switch statements always do N things.

Unfortunately we can’t always avoid switch statements, but we can make sure that each switch statement is buried in a low-level class and is never repeated.

Switch Statements

It’s hard to make a small switch statement.

It’s also hard to make a switch statement that does one thing.

Use Descriptive Names

Don’t be afraid to make a name long.

A long descriptive name is better than a short mysterious name.

A long descriptive name is better than a long descriptive comment.

The ideal number of arguments for a function is zero.

- Asking a question about that argument, as in  isFileExists(“File”).

- Or you may be operating on that argument, transforming it into something else and returning it.

-  An event. passwordAttemptFailedNtimes(attempts).

Common Monadic Forms (1 argument)

Try to avoid any monadic functions that don’t follow these forms

A function with two arguments is harder to understand than a monadic function.

Dyadic Functions

Point p = new Point(0,0); is perfectly reasonable

However, the two arguments in this case are ordered components of a single value!

Dyadic Functions

Even obvious dyadic functions like  assertEquals(expected, actual) are problematic.

How many times have you put the  actual where the  expected should be

Dyadic Functions

Dyads aren’t evil, and you will certainly have to write them.

However, you should be aware that they come at a cost

Dyadic Functions

Functions that take three arguments are significantly harder to understand than dyads.

Triads

George Boole, inventor of the Boolean logic.

Flag Arguments

Passing a boolean into a function is a truly terrible practice.

The method call  render(true) is just plain confusing to a poor reader

We should have split the function into two:  renderForSuite() and  renderForSingleTest() 

widget.repaint(false);

Guess what this code means?

The usual suspect is don’t repaint the widget.

Then, you look at the documentation

widget.repaint(false);

 It refers the function argument as immediate, which is true if you want immediate painting or false for deferred painting.

The above code is actually repaint this widget later, which is miles away from your initial guess.

widget.repaint({ immediate: false });
widget.repaint({ mode: "immediate" });
widget.repaintLater();

What can we do?

var opacitySlider = new Slider(true);

Mysteriously, there are also lines similar to:

Let's say your user interface needs a bunch of sliders to allow the user to choose some values.

var volumeSlider = new Slider(false);

It turns out that true there means a horizontal slider and false means a vertical slider.

var opacitySlider = new HorizontalSlider();
var volumeSlider = new VerticalSlider();
var mysteriousSlider = new DiagonalSlider();

You may scream, "Of course, I won’t be too idiot to make those rookie mistakes!"

stackView.updateHeight(false);

Yes, that false again refers to immediate or not

widget.next(true);

True says that the very first child widget will be returned if you hit the last one.

widget.destroy(false);

Which potentially leads you to think don’t destroy this widget.

You can’t be more wrong

Actually still destroys your widget, but it leaves the DOM associated with the widget intact.

While one boolean argument is already confusing, two boolean arguments can’t be more fun.

cmp.setCentered(true, false);

A trip to the API doc enlightens the reviewer that the function signature is actuallysetCentered(centered, autoUpdate)

menu.stop(true, false);

The boolean values there refer to clear the animation queue or not and go to the animation or not, respectively.

Argument Objects

When a function seems to need more than two or three arguments, it is likely that some of those arguments ought to be wrapped into a class of their own. 

Circle makeCircle(double x, double y, double radius);
Circle makeCircle(Point center, double radius);

Verbs and Keywords

For example, write(name) is very expressive.

Whatever this “name” thing is, it is being “written.”

An even better name might be writeField(name)

This is an example of the keyword form of a function name.

For example, assertEquals might be better written as assertExpectedEqualsActual(expected, actual).

Have No Side Effects

Side effects are lies. Your function promises to do one thing, but it also does other hidden things

Command Query Separation

Functions should either do something or answer something, but not both. 

Doing both often leads to confusion.

Prefer Exceptions to Returning Error Codes

When you return an error code, you create the problem that the caller must deal with the error immediately.

On the other hand, if you use exceptions instead of returned error codes, then the error processing code can be separated from the happy path code.

Extract Try/Catch Blocks

Try/catch blocks confuse the structure of the code and mix error processing with normal processing. So it is better to extract the bodies of the try and catch blocks out into functions of their own.

public void delete(Page page) {
  try {
   deletePage(page);
   registry.deleteReference(page.name);
   configKeys.deleteKey(page.name.makeKey());
  }
  catch (Exception e) {
   logger.log(e.getMessage());
  }
}

Extract Try/Catch Blocks

Try/catch blocks confuse the structure of the code and mix error processing with normal processing. So it is better to extract the bodies of the try and catch blocks out into functions of their own.

public void delete(Page page) {
  try {
   deletePageAndAllReferences(page);
  }
  catch (Exception e) {
   logError(e);
  }
}

Error Handling Is One Thing

Functions should do one thing. Error handing is one thing. Thus, a function that handles errors should do nothing else. 

Don’t Repeat Yourself

Duplication may be the root of all evil in software.

Many principles and practices have been created for the purpose of controlling or eliminating it.

function cookSalad()
{
    prepare('asparagus')
    prepare('broccoli')
    prepare('cabbage')
}
function cookSalad()
{
    vegetables = ['asparagus', 'broccoli', 'cabbage']

    for(ingredient : vegetables)
    { 
        prepare(ingredient)
    }
}

DO NOT EVER COPY AND PASTE CODE

Structured Programming

Dijkstra said that every function, and every block within a function, should have one entry and one exit. Following these rules means that there should only be one return statement in a function, no break or continue statements in a loop, and never, ever, any goto statements. 

Edsger W. Dijkstra

was an early theoretical pioneer in many areas of computing science including algorithm design, programming methodology, and software architecture.

Comments

“Don’t comment bad code—rewrite it.”

—Brian W. Kernighan and P. J. Plaugher

 The proper use of comments is to compensate for our failure to express our-self in code.

Comments are always failures. We must have them because we cannot always figure out how to express ourselves without them. 

Why are they so evil

Programmers can’t realistically maintain them. 

Not always, and not intentionally, but too often. 

Code changes and evolves but usually we forget about changing comments.

Inaccurate comments are far worse than no comments at all.

Truth can only be found in one place: the code.

Comments Do Not Make Up for Bad Code

Rather than spend your time writing the comments that explain the mess you’ve made, spend it cleaning that mess.

“Ooh, I’d better comment that!” No! You’d better clean it!

Explain Yourself in Code

In many cases it’s simply a matter of creating a function that says the same thing as the comment you want to write.

Which would you rather see?

Good Comments

Legal Comments

Copyright and authorship statements 

Explanation of Intent

Sometimes a comment goes beyond just useful information about the implementation and provides the intent behind a decision

Warning of Consequences

Sometimes it is useful to warn other programmers about certain consequences.

Amplification

A comment may be used to amplify the importance of something

TODO Comments

It is sometimes reasonable to leave “To do” notes 

Bad Comments

Mumbling

Plopping in a comment just because you feel you should or because the process requires it

Nonlocal Information

If you must write a comment, then make sure it describes the code it appears near. Don’t offer systemwide information in the context of a local comment.

Too Much Information

Don’t put interesting historical discussions or irrelevant descriptions of details into your comments.

Inobvious Connection

The connection between a comment and the code it describes should be obvious. 

What is a filter byte?

Does it relate to the +1?

Or to the *3? Both? Is a pixel a byte? Why 200?

  • Redundant Comments
  • Misleading Comments
  • Mandated Comments
  • Journal Comments
  • Commented-Out Code
  • Noise Comments
  • Position Markers
  • Closing Brace Comments
  • Attributions and Bylines

​And there are more...

Formatting

How big should a source file be?

It turns out that there is a huge range of sizes and some remarkable differences in style.

The topmost parts of the source file should provide the high-level concepts and algorithms.

Detail should increase as we move downward, until at the end we find the lowest level functions and details in the source file.

The Newspaper Metaphor

 The name should be simple but explanatory.

 We would like a source file to be like a newspaper article.

Vertical Openness Between Concepts

Each line represents an expression or a clause, and each group of lines represents a complete thought. Those thoughts should be separated from each other with blank lines. 

Vertical Density

If openness separates concepts, then vertical density implies close association. So lines of code that are tightly related should appear vertically dense.

Variables should be declared as close to their usage as possible.

Vertical Distance

Dependent Functions. If one function calls another, they should be vertically close, and the caller should be above the callee, if at all possible. 

Vertical Distance

How wide should a line be?

Every size from 20 to 60 represents about 1 percent of the total number of lines. That’s 40 percent.

The old Hollerith limit of 80 is a bit arbitrary, and I’m not opposed to lines edging out to 100 or even 120

Horizontal Formatting

Horizontal Alignment

 

The alignment seems to emphasize the wrong things and leads my eye away from the true intent.

A source file is a hierarchy rather like an outline.

Indentation

To make hierarchy of scopes visible, we indent the lines of source code in proportion to their position in the hiearchy.

Team Rules

A team of developers should agree upon a single formatting style

 

Objects and Data Structures

 

There is a reason that we keep our variables private.

Data Abstraction

Why, then, do so many programmers automatically add getters and setters to their objects, exposing their private variables as if they were public?

We don’t want anyone else to depend on them. We want to keep the freedom to change their type or implementation on a whim or an impulse.

Data Abstraction

Rather it exposes abstract interfaces that allow its users to manipulate the essence of the data, without having to know its implementation.

Hiding implementation is not just a matter of putting a layer of functions between the variables.

Hiding implementation is about abstractions! A class does not simply push its variables out through getters and setters.

 Objects hide their data behind abstractions and expose functions that operate on that data.

Data structure expose their data and have no meaningful functions.

Objects vs Data Structures

Go back and read that again.

Objects vs Data Structures

OO code

On the other hand, makes it easy to add new classes without changing existing functions.

 

Procedural code

(code using data structures) Makes it easy to add new functions without changing the existing data structures.

 

The Law of Demeter

It says a module should not know about the innards of the objects it manipulates.

Law of Demeter says that a method f of a class C should only call the methods of these:

  • C
  • An object created by f
  • An object passed as an argument to f
  • An object held in an instance variable of C

The method should not invoke methods on objects that are returned by any of the allowed functions. In other words, talk to friends, not to strangers.

Train Wrecks

final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
Options opts = ctxt.getOptions();
File scratchDir = opts.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();

It is usually best to split them up as follows:

Whether this is a violation of Demeter depends on whether or not ctxt, Options, and ScratchDir are objects or data structures.

 Chains of calls like this are generally considered to be sloppy style and should be avoided.

Hybrids

Confusion between objects or data structures sometimes leads to unfortunate hybrid structures that are half object and half data structure.

They are the worst of both worlds. Avoid creating them.

They are indicative of a muddled design whose authors are unsure of—or worse, ignorant of—whether they need protection from functions or types.

Such hybrids make it hard to add new functions but also make it hard to add new data structures.

Data Transfer Objects

The quintessential form of a data structure is a class with public variables and no functions. 

They are very useful structures, especially when communicating with databases.

 

This is sometimes called a data transfer object, or DTO.

Data Transfer Objects

This is awkward because it creates a hybrid between a data structure and an object.

Unfortunately we often find that developers try to treat these data structures as though they were objects by putting business rule methods in them.

Error handling is important, but if it obscures logic, it’s wrong.

Michael Feathers

Error Handling

Use Exceptions Rather Than Return Codes

 

Provide Context with Exceptions

Each exception that you throw should provide enough context to determine the source and location of an error.

Define Exception Classes in Terms of a Caller’s Needs

 

There are many ways to classify errors.

However, when we define exception classes in an application, our most important concern should be how they are caught.

try{
    ScockedSimple::open();   
} catch(DeviceResponseException $e) {
    reportError($e);
    $logger->log("Device response exception", $e);
} catch (AT3LockException $e) {
    reportError($e);
    $logger->log("Device response exception", $e);
} catch (XMGError $e) {
    reportError($e);
    $logger->log("Device response exception", $e);
} finaly {
    …
}
try {
    ScockedSimple::open();
} catch (PortDeviceFailure $e) {
    reportError($e);
    $logger->log($e->getMessage(), $e);
} finally {
    …
}

Define the Normal Flow

try {
    MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
    m_total += expenses.getTotal();
} catch(MealExpensesNotFound e) {
    m_total += getMealPerDiem();
}

We can change the ExpenseReportDAO so that it always returns a  MealExpense object. If there are no meal expenses, it returns a  MealExpense object that returns the per diem as its total

Define the Normal Flow

MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
m_total += expenses.getTotal();

This is called the SPECIAL CASE PATTERN [Fowler].

You create a class or configure an object so that it handles a special case for you.

Don’t Return Null

 
public void registerItem(Item item) {
    if (item != null) {
        ItemRegistry registry = peristentStore.getItemRegistry();
        if (registry != null) {
            Item existing = registry.getItem(item.getID());
            if (existing.getBillingPeriod().hasRetailOwner()) {
                existing.register(item);
            }
        }
    }
}

Did you notice the fact that there wasn’t a  null check in the second line of that nested if statement?

 

Unit Tests

By now everyone knows that TDD asks us to write unit tests first, before we write production code. But that rule is just the tip of the iceberg.

Consider the following three laws:

  1. You may not write production code until you have written a failing unit test.
  2. You may not write more of a unit test than is sufficient to fail.
  3. You may not write more production code than is sufficient to pass the currently failing test.
 

The Three Laws of TDD

If we work this way, we will write dozens of tests every day.

The size of tests can rival the size of the production code itself

And this can present a daunting management problem.

 

Keeping Tests Clean

Having dirty tests is equivalent to, if not worse than, having no tests.

Test code is just as important as production code. It is not a second-class citizen. It requires thought, design, and care. It must be kept as clean as production code.

If you don’t keep your tests clean, you will lose them.

What makes a clean test?

Three things.

Readability, 

readability, 

 and readability.

They should run quickly. When tests run slow, you won’t want to run them frequently. If you don’t run them frequently, you won’t find problems early enough to fix them easily. 

Fast

Tests should not depend on each other.

Independent 

Tests should be repeatable in any environment. If your tests aren’t repeatable in any environment, then you’ll always have an excuse for why they fail.

Repeatable

Classes

Encapsulation

We like to keep our variables and utility functions private.

Loosening encapsulation is always a last resort.

Classes Should Be Small!

But we don't measure size! 

We should also be able to write a brief description of the class in about 25 words, without using the words “if,” “and,” “or,” or “but.”

We want our systems to be composed of many small classes, not a few large ones.

We count responsibilities.

The Single Responsibility Principle

It states that a class or module should have one, and only one, reason to change.

Cohesion

We would like cohesion to be high. A class in which each variable is used by each method is maximally cohesive.

When classes lose cohesion, split them!

 

Interface Segregation Principle

It states that clients should not be forced to implement interfaces they don't use. Instead of one fat interface many small interfaces are preferred based on groups of methods, each one serving one submodule.

Organizing for Change

In a clean system we organize our classes so as to reduce the risk of change.

Isolating from Change

Needs will change, therefore code will change. A client class depending upon concrete details is at risk when those details change.

The Open/Closed Principle

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

System

Separate Construction of a System from Using It

 

First, consider that construction is a very different process from use.

Separate Constructing a System from Using It

Here is a typical example:

public Service getService()
{
    if(service == null)
    {
        service = new MyServiceImp(...);
    }
    return service;
}

Unfortunately, most applications don’t separate this concern. 

Separate Constructing a System from Using It

This is the lazy Initialization/evaluation idiom, and it has several merits.

public Service getService()
{
    if(service == null)
    {
        service = new MyServiceImp(...);
    }
    return service;
}

But still there are a lot of disadvantages:

  • Hard-coded dependency on MyServiceImpl and all its constructor requires
  • Testing can be a problem
  • We don't know whether MyServiceImpl is the right object in all cases.

Separate Constructing a System from Using It

public Service getService()
{
    if(service == null)
    {
        service = new MyServiceImp(...);
    }
    return service;
}

One occurrence isn't a serious problem. However, there are normally many setup isioms like this in applications. Therefore, the global setup strategy is scattered across the application.

We should never let
little, convenient idioms lead to modularity breakdown.

Separation of Main

This is the simplest way to separate construction from use.

Simply move all aspects of construction to modules called main, and design the rest of the system assuming that all objects have been constructed and wired up appropriately.

Factories

Sometimes, of course, we need to make the application responsible for when an object gets created.

Again notice that all the dependencies point from main toward the OrderProcessing application.

Dependency Inversion Principle

A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
B. Abstractions should not depend upon details. Details should depend upon abstractions.

Dependency Inversion Principle

The most common, and most frequently used solution to invert the dependency is to introduce a more abstract module in our design. "The most abstract element in OOP is an Interface. Thus, any other class can depend on an Interface and still respect DIP".

Dependency Inversion Principle

 
  • Almost force you into respecting OCP.
  • Allow you to separate responsibilities.
  • Make you correctly use subtyping.
  • Offer you the opportunity to segregate your interfaces.
public class Program 
{
    public static void Main()
    {
        var reporter = new Reporter();
        reporter.SendReports();
    } 
}
public class Reporter 
{
     public void SendReports()
     {
        var reportBuilder = new ReportBuilder();
        reports = reportBuilder.CreateReports();
  
        var reportSender = new EmailReportSender();
        foreach (Report report in reports)
        {
            reportSender.Send(report);
        }
    }
}

DIP example

public static void Main()
{
     var builder = new ReportBuilder();
     var sender = new SmsReportSender();
     var reporter = new Reporter(builder, sender);
  
     reporter.SendReports();
}

DIP example

public class Reporter 
{
    public Reporter(IReportBuilder reportBuilder, IReportSender reportSender)
    {
         this.reportBuilder = reportBuilder;
         this.reportSender = reportSender;
    }

    public void SendReports()
    {
        reports = reportBuilder.CreateReports();
  
        foreach (Report report in reports)
        {
            reportSender.Send(report);
        }
    }
}

Dependency Injection

Inversion of Control moves secondary responsibilities from an object to other objects that are dedicated to the purpose, thereby supporting the Single Responsibility Principle.

Thre are at least two types of dependency injection:

Constructor injection:

the dependencies are provided through a class constructor.

Setter injection:

the client exposes a setter method that the injector uses to inject the dependency.

It is possible for DI frameworks to have other types of injection beyond those presented above

  • String
  • Object
  • Closures/Anonymous functions
  • Constructor Injection
  • Setter Injection
  • Properties Injection
  • Shared services

Phalcon\DI

Phalcon\DI is a component implementing Dependency Injection and Location of services and it’s itself a container for them.

You can register service in the Container by:

Symfony\Component\DependencyInjection\Container

Is another great implementation of Dependency Injection.

List of the major features that he has.

  • Container Configuration Files
  • Constructor Injection
  • Setter Injection
  • Property Injection
  • Tagged Services
  • Compiling the Container
  • Dumping the Configuration
  • Using a Factory to Create Services
  • Configuring Services with a Service Configurator
  • Managing Common Dependencies with Parent Services
  • Aliasing
  • Decorating Service

Robert Cecil Martin

Made with Slides.com