Saturday, March 2, 2024
HomeJavaCurly Braces #11: Writing SOLID Java code

Curly Braces #11: Writing SOLID Java code


Oracle Java Certification, Java Preparation, Java Guides, Java Tutorial and Materials, Java Learning

On this article, I’m going to speak about writing SOLID Java code. No, I’m not speaking in regards to the glorious, basic e-book by Steve Maguire, Writing Stable Code, now out in its twentieth anniversary second version. Moderately, I’m speaking in regards to the SOLID ideas of object-oriented design (OOD), as taught by Robert C. Martin.

When my very own journey started, I used to be taught to put in writing numerous procedural code in meeting language and C. As a pc science pupil and at my first skilled programming job, I wrote a mixture of procedural and useful C. The article-oriented programming (OOP) motion was in full swing after I moved from C to C++, and I embraced OOP utterly earlier than shifting to Java.

Nonetheless, a couple of issues occurred: the expansion of SQL databases, the emergence of the World Vast Internet, and the expansion of automation. With these got here SQL, HTML, and bash, Python, Perl, PHP, JavaScript scripting languages, and others. These have been all extra useful than crucial—and positively extra useful than object oriented.

Purposeful programming is predicated on mathematical ideas, and it depends on clearly defining inputs and outputs, embracing ideas such because the discount of uncomfortable side effects, immutability, and referential transparency. Additional, useful programming is commonly related to declarative programming, the place you describe what you need the pc to do, versus telling the pc precisely do it, by structuring your code round real-world objects and mimicking their habits in an crucial approach.

Whereas Java is essentially an crucial language, Java has embraced useful programming and the declarative nature that always comes with it.

Simply as you are able to do OOP with C, you are able to do useful programming in Java. For instance, objects assist outline boundaries and carry out grouping that may be helpful in any kind of programming; this idea works for useful programming as effectively. And simply as objects and inheritance (or composition) help you divide and conquer, by build up from smaller items of implementation in OOP, you’ll be able to construct advanced algorithms from many smaller capabilities.

Doing Java a SOLID

Perhaps OOP ideas aren’t fairly useless but, and mature canine reminiscent of Java are able to new useful tips. However SOLID is extra about design than programming. For instance, I might counsel that even pure useful programmers suppose in objects, giving entities and elements names that describe what they do as a lot as what they’re. For instance, perhaps you will have a TradingEngine, an Orchestrator, or a UserManager element.

Whatever the paradigm, everybody needs code to be comprehensible and maintainable. SOLID, which is made from the next 5 ideas from which its title derives, fosters these exact same objectives:

  • SRP: The one-responsibility precept
  • OCP: The open-closed precept
  • LSP: The Liskov substitution precept
  • ISP: The interface segregation precept
  • DIP: The dependency inversion precept

Subsequent, I’ll talk about these ideas within the context of Java improvement.

Single-responsibility precept

SRP states {that a} class ought to have a single accountability or objective. Nothing else ought to require the category to vary exterior of this objective. Take the Java Date class, for instance. Discover there are not any formatting strategies out there in that class. Moreover, older strategies that got here near formatting, reminiscent of toLocaleString, have been deprecated. It is a good instance of SRP.

With the Date class, there are concise strategies out there for creating objects that signify a date and for evaluating two completely different Date objects (and their consultant dates by way of time). To format and show a Date object, it’s good to use the DateFormat class, which bears the accountability to format a given Date in response to a set of versatile standards. For instance, for this code,

Date d = new Date();

String s = DateFormat.getDateInstance().format(d);

System.out.println(s);

the output can be

July 4, 2023

If it’s good to change how dates are represented inside the system, you modify solely the Date class. If it’s good to change the way in which dates are formatted for show, you modify simply the DateFormat class, not the Date class. Combining that performance into one class would create a monolithic behemoth that may be prone to uncomfortable side effects and associated bugs in case you modified one space of accountability inside the single codebase. Following the SOLID precept of SRP helps keep away from these issues.

Open-closed precept

As soon as a category is full and has fulfilled its objective, there could also be a motive to increase that class however you shouldn’t modify it. As a substitute, it is best to use generalization in some type, reminiscent of inheritance or delegation, as an alternative of modifying the supply code.

Have a look at the DateFormat class’s Javadoc, and also you’ll see that DateFormat is an summary base class, successfully imposing OCP. Whereas DateFormat has strategies to specify a time zone, point out the place to insert or append a formatted date right into a given StringBuffer, or deal with sorts of calendars, SimpleDateFormat extends DateFormat so as to add extra elaborate pattern-based formatting. Shifting from DateFormat to SimpleDateFormat offers you every part you had earlier than—and an entire lot extra. For instance, for the next code,

Date d = new Date();

SimpleDateFormat sdf = 

    new SimpleDateFormat(“YYYY-MM-dd HH:MM:SS (zzzz)”);

String s = sdf.format(d);

System.out.println(s);

the output can be

2023-07-04 09:07:722 (Jap Daylight Time)

The necessary level is that the DateFormat class is left untouched from a source-code perspective, eliminating the possibility to adversely have an effect on any dependent code. As a substitute, by extending the category, you’ll be able to add new performance by isolating adjustments in a brand new class, whereas the unique class continues to be out there and untouched for each current and new code to make use of.

Liskov substitution precept

LSP, developed by Barbara Liskov, states that if code works with a given class, it should proceed to work appropriately with subclasses of that base class. Though LSP sounds easy, there are all kinds of examples that present how onerous it’s to implement and check LSP.
One widespread instance entails shapes with a Form base class. Rectangle and Sq. subclasses behave in another way sufficient that substituting subclasses and requesting the world might yield sudden outcomes.

public class LSPTest {

    static AbstractQueue<MyDataClass> q = 

            new ArrayBlockingQueue(100);

            //new DelayQueue();

    

    public static void most important(String[] args) throws Exception {

        for ( int i = 0; i < 10; i++ ) {

            q.add( getData(i+1) );

        }

        MyDataClass first = q.ingredient();

        System.out.println(“First ingredient information: ” +first.val3);

        

        int i = 0;

        for ( MyDataClass information: q ) {

            if ( i++ == 0 ) {

                check(information, first);

            }

            System.out.println(“Knowledge ingredient: ” + information.val3);

        }

        

        MyDataClass information = q.peek();

        check(information, first);

        int parts = q.measurement();

        information = q.take away();

        check(information, first);

        if ( q.measurement() != elements-1 ) {

            throw new Exception(“Failed LSP check!”);

        }

        

        q.clear();

        if ( ! q.isEmpty() ) {

            throw new Exception(“Failed LSP check!”);

        }

    }

    

    public static MyDataClass getData(int i) {

        Random rand = new Random(i); 

        MyDataClass information = new MyDataClass();

        information.val1 = rand.nextInt(100000);

        information.val2 = rand.nextLong(100000);

        information.val3 = “”+information.val1+information.val2;

        return information;

    }

    

    public static void check(MyDataClass d1, MyDataClass d2) throws Exception{

        if ( ! d1.val3.equals(d2.val3) ) {

            throw new Exception(“Failed LSP check!”);

        }

    }

}

However my code doesn’t move the LSP check! It fails for 2 causes: The habits of add within the DelayQueue class requires the weather to implement the Delayed interface, and even when that’s mounted, the implementation of take away has been, effectively, eliminated. Each violate LSP.

I did discover, nonetheless, that AbstractBlockingQueue and ConcurrentLinkedQueue handed the LSP exams. That is good, however I hoped there would have been extra consistency.

Interface segregation precept

With OOP, it’s simple to get carried away. For instance, you’ll be able to create a Doc interface after which outline interfaces that signify different paperwork reminiscent of textual content paperwork, numeric paperwork (reminiscent of a spreadsheet), or presentation-style paperwork (reminiscent of slides). That is advantageous, however the temptation so as to add habits to those already wealthy interfaces can add an excessive amount of complexity.

For instance, assume the Doc interface defines fundamental strategies to create, retailer, and edit paperwork. The following evolution of the interface could be so as to add formatting strategies for a doc after which so as to add strategies to print a doc (successfully telling a Doc to “print itself”). On the floor, this is sensible, as a result of all of the habits coping with paperwork is related to the Doc interface.

Nonetheless, when that’s carried out, it means all the code to create paperwork, retailer paperwork, format paperwork, print paperwork, and so forth—every an space of considerable complexity—comes collectively in a single implementation that may have drawbacks in relation to upkeep, regression testing, and even construct instances.

ISP breaks these megainterfaces aside, acknowledging that making a doc is a really completely different implementation from formatting a doc and even printing a doc. Growing every of these areas of performance requires its personal middle of experience; subsequently, every needs to be its personal interface.

As a byproduct, this additionally signifies that different builders needn’t implement interfaces that they by no means intend to assist. For instance, you’ll be able to create a slide deck after which format it to solely be shared by way of e-mail and by no means printed. Otherwise you would possibly resolve to create a easy text-based readme file, versus a richly formatted doc used as advertising materials.

An excellent instance of ISP in observe within the JDK is the java.awt set of interfaces. Take Form, for instance. The interface could be very centered and easy. It incorporates strategies that test for intersection, containment, and assist for path iteration of the form’s factors. Nonetheless, the work of truly iterating the trail for a Form is outlined within the PathIterator interface. Additional, strategies to attract Form objects are outlined in different interfaces and summary base courses reminiscent of Graphics2D.

On this instance, java.awt doesn’t inform a Form to attract itself or to have a colour (itself a wealthy space of implementation); it’s a very good working instance of ISP.

Dependency inversion precept

It’s pure to put in writing higher-level code that makes use of lower-level utility code. Perhaps you will have a helper class to learn recordsdata or course of XML information. That’s advantageous, however most often, reminiscent of code that connects to databases, JMS servers, or different lower-level entities, it’s higher to not code to them instantly.

As a substitute, DIP says that each lower-level and higher-level constructs in code ought to by no means rely instantly on each other. It’s greatest to position abstractions, within the type of extra basic interfaces, in between them. For instance, within the JDK, have a look at the java.sql.* bundle or the JMS API and set of courses.

With JDBC, you’ll be able to code to the Connection interface with out realizing precisely which database you’re connecting to, whereas lower-level utility code, reminiscent of DriverManager or DataSource, hides the database-specific particulars out of your utility. Mix this with dependency injection frameworks reminiscent of Spring or Micronaut, and you’ll even late-bind and alter the database kind with out altering any code.

Within the JMS API, the identical paradigm is used to join to a JMS server, however it goes even additional. Your utility can use the Vacation spot interface to ship and obtain messages with out realizing whether or not these messages are delivered by way of a Subject or a Queue. Decrease-level code could be written or configured to decide on the message paradigm (Subject or Queue) with related message supply particulars and traits hidden, and your utility code by no means wants to vary. It’s abstracted by the Vacation spot interface in between.

You must write stable SOLID code

SOLID is a wealthy and deep subject; there’s much more to find out about. You can begin small. Discover solace realizing that the JDK has embraced most of the OOD ideas that make writing code extra environment friendly and productive.

Supply: oracle.com

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments