DEV Community

Krystian
Krystian

Posted on • Originally published at kwojcicki.github.io

The DRY Principle Reexamined

DRY Principle Reexamined

Most programmers self taught or university educated, have at one time come into contact with the DRY Principle. Its often introduced as a segway into methods and OOP. For example, if across your code base you calculate the price of a shopping list multiple times:

    void method1(){
        ...
        for(Item item: items){
            price += item.cost;
        }
        price *= 1.13;
        price -= customer.discount();
        ...
    }

    void method2(){
        ...
        for(Item item: items){
            price += item.cost;
        }
        price *= 1.13;
        price -= customer.discount();
        ...
    }

You're taught to encapsulate that logic into a method. This helps keep your code base small, clean and easier to maintain.

    int calculateCost(Item[] items, Customer customer){
        int cost = 0;
        for(Item item: items){
            cost += item.cost;
        }
        price *= 1.13;
        price -= customer.discount();
        return cost;
    }

This is all fine and dandy and most programmers are comfortable with this. However the DRY principle doesn't just apply to your code, it also applies to the standard library and library's you are utilizing.

Lets start off with an example where we want to create a function that returns the difference between the max and min element in an array.

int maxmin_difference(int[] array){
    int min = Integer.MAX_VALUE;
    int max = Integer.MIN_VALUE;

    for(int i: array){
        min = Math.min(min, i);
        max = Math.max(max, i);
    }

    return max - min; // not worrying about integer overflow
}

Most developers if they saw this in code review would be happy, its relatively straight forward and intuitive. However I would argue this violates the DRY Principle, the Java standard library already comes with a Collections.max() and Collections.min() function.

int maxmin_difference(List<Integer> array){
    return Collections.max(array) - Collections.min(array); // not worrying about integer overflow
}

Utilizing those methods will clean up our code and remove the nuances around initializing min/max correctly.

PS: Much of the inspiration of this post, including this example, comes from a fantastic talk given by Conor Hoekstra.

Relying on battle hardened and extensively tested code is the best way to prevent bugs and from violating DRYOTCAY (Don't Repeat Yourself Or the Code Around You).

Imagine the software you are building as a construction of Lego Blocks, whenever you pick a piece you can either use an official Lego Block ™ or a self made Lego Block.

Lego blocks

When using the official Lego Blocks's you know its structurally sound and does the job (like standard libraries that come with languages), these blocks are the preferred material choice. However sometimes a little magic/glue needs to be added to get those final touches just right. The more self made blocks are added the more your structures integrity is unknown.

Ideally your program would just one line that calls the standard library.

public static void main(String[] args){
    Library.doMyCoolStuff(args);
}

Unfortunately this is unlikely to be the case and custom code will need to be written. This is perfectly fine, but attempting to reinvent the wheel for everything when a function/library already exists is a BAD DECISION. Instead really learn your languages standard library and the ecosystem of libraries available to you. As well when Googling for help, don't fall into the XY Problem, your problem is likely not as unique as you think it is.

Copied over from http://kwojcicki.github.io/

Top comments (1)

Collapse
 
awwsmm profile image
Andrew (he/him)

DRYOTCAY

Catchy!

Seriously, though, some good advice here. My #1 rule for code cleanliness is just to use the standard libraries the way they're designed. As you say, there are built-in methods to find array maxima and minima, so why reinvent the wheel?