Free Online Courses for Software Developers - MrBool
× Please, log in to give us a feedback. Click here to login
×

You must be logged to download. Click here to login

×

MrBool is totally free and you can help us to help the Developers Community around the world

Yes, I'd like to help the MrBool and the Developers Community before download

No, I'd like to download without make the donation

×

MrBool is totally free and you can help us to help the Developers Community around the world

Yes, I'd like to help the MrBool and the Developers Community before download

No, I'd like to download without make the donation

Restrictive Programming: Best Practices in Java

This article will explore some aspects of programming with restrictions, and their importance to the creation of more secure applications

After years of developing software, many programmers end up realizing that there are certain heuristics that, if implemented, result in more concise and solid code. The purpose of this article is to catalog some of these good practices, many experienced programmers know, even unconsciously, and may help those who are entering the area.

Applying the code renormalizations

In physics, renormalization is a technique to simplify calculations, eliminating the presence of infinite in the equations. It is a way of trying to tame the complexity of the problem domain, reducing it to a narrower scope.

This technique can also be applied in our daily schedule, making our code safer.

Enumeration

Use constant int, String or any other primitive type to represent a finite and small set of states or values, instead of using Enumeration, is a mistake that many programmers make. Let's look at this example in Listing 1.

Listing 1. Code Enum

  public static final String APPROVED = "Approved";
  public static final String REPROVED = "Reproved";
  public static final String PENDING = "Pending";
  public void doSomething(String status) {
    // ...
  }

Let's assume that the doSomething method should only accept one of three states: APPROVED, REPROVED and PENDING. For any input it would cause an exception. What's wrong with this code?

As the argument of the method is of type String, any String could be passed, even if not part of the set of entries that the developer of the method want . There is no formal restriction to ensure the correct use of the method, so we have multiple input possibilities. To eliminate this "infinity", just apply a 'renormalization "in the case, the use of Enumeration instead of String constants, as shown in Listing 2.

Listing 2. Code with Enum

 enum Status {
   APPROVED,
   REPROVED,
   PENDING
}
public void doSomething(Status status)  { //... }

Despite being a simple example, it has important implications:

  • We restrict method of entry to only three possible states, instead of "infinite";
  • The compiler will work in our favor, as do compile-time type checking is one of the best techniques to prevent programmers mistakenly use a code snippet. We should, whenever possible, use the language features to make the compiler ensure us the code promises.

Tiny Types and Strings Oriented Programming

We will see in Listing 3 more interesting case of application of renormalization and secure verification by the compiler.

Listing 3. Class Person

 public class Person {
      
      private String name;
      private String surname;
      private String rg;
      
      public Person(String name) {
          this.name = name;
      }
      
      public Person(String name, String surname) {
          this.name = name;
          this.surname = surname;
      }    
      
      public Person(String x, String y, String z) {
          this.name = x;
          this.surname = y;
          this.rg = z;
      }       
  }

This is the typical model of data oriented Strings. In addition to the myriad of problems, the compiler will not be willing to save things like you:

new Person("De Niro"); // Last name instead of the name
new Person("99999999-99") // GR rather than the name
new Person("Fowler" "Martin"); // Inversion

Any unsuspecting programmer could make the mistake of reversing values, pass a semantically wrong value, etc. But how to prevent these errors in the compiler?

Listing 4. Tiny Types

 public class Name {
     private String name;
     public Name(String text) { this.name = text; }
  }
      
  public class Surname {
      private String surname;
      public Surname(String text) { this.surname = text; }
  }
      
  public class RG {
      private String rg;
      public RG(String text) { this.rg = text; }
  }
   
  public class Person {    
     
      private Name name;
      private Surname surname;
      private RG rg;
      
      public Person(Name x) {
          this.name = x;
      }
      
      public Person(Name x, Surname y) {
          this.name = x;
          this.surname = y;
      }    
      
      public Person(Name x, Surname y, RG z) {
          this.name = x;
          this.surname = y;
          this.rg = z;
      }       
  }

Using more robust types (instead of strings) in your data model, as shown in Listing 4, the mistakes made in the example of construction Person objects would be advised at compile time, providing greater security in the use of this class.

Yes, more code must be written to support this programming model (increases the verbosity of the code), and the work to make the changes to other data types in the persistence layer or the use of certain frameworks. However, the gain in terms of compile-time type safety, and consequently less chances of error by the developer in production are aspects to consider. Another benefit is that the code becomes more descriptive and intuitive.

Moreover, we could make these richer classes. For example, if there was a CPF class, we could include in it a CPF validation method. The class name could validate entries that include numbers, remove spacings at the beginning and end, impose a maximum size, etc.

Builder pattern

Builder pattern is a great tool when we want certain objects have their most important attributes initialized, thus avoiding the famous NullPointerException errors or SQL errors when the field in the table that corresponds to the field of the class is the type required (although this validation can be made in the persistence layer).

See an example in Listing 5.

Listing 5. Required fields and options

 public class Person {    
      private String name;  // Must be null | required at db
      private String surname;  // Must be null | required at db
      private String rg;   // Optional
      private String cpf;   // Optional
      ...
  }

But how to make the compiler help us meet these premises, without relying on the goodwill of the developer? Let's see Listing 6.

Listing 6. Use standard Builder to ensure the premises of the Person class

 // Classe Person becomes imutable
  public final class Person {    
   
      private final String name;  
      private final String surname;
      private final String rg;   
      private final String cpf;   
       
      public static class Builder {
          // Required parameters
          private final String name;    
          private final String surname;
   
          // Optional parameters - initialized to default values
          private String rg  = ""; // NULL safe
          private String cpf  = ""; // NULL safe
   
          public Builder(String name, String surname) {
              this.name = name;
              this.surname = surname;
          }
   
          public Builder rg(String val)
              { rg = val;   return this; }
          public Builder cpf(String val)
              { cpf = val;  return this; }
          
          public Person build() {
              return new Person(this);
          }
      }
   
      private Person(Builder builder) {
          name       = builder.name;
          surname  = builder.surname;
          rg         = builder.rg;
          cpf        = builder.cpf;
      }
      
      public static void main(String... args) {
          Person person1 = new Person.Builder("Robert", "De Niro").build();
   
          // + cpf
          Person person2 = new Person.Builder("Robert", "De Niro").cpf("12345").build();
   
          // + cpf and rg
          Person person3 = new Person.Builder("Robert", "De Niro").cpf("12345").rg("00000").build();        
      }
  } 

The Builder pattern, in addition to helping startup/creating classes with many attributes, can be used to enforce usage restrictions for developers, ensuring the correct use of certain classes in the application.

Realize that, regardless of the technique discussed (Enumeration, Tiny Types or Builder), the real goal is to impose restrictions that are checked by the compiler, preventing developers break assumptions that would otherwise be vague and easily forgotten.

Talking about restrictions, we need to mention the appearance of immutability. Even in the previous example, the presence of immutability is fundamental to, along with the standard Builder, ensure the premises of the Person class.

Optional - Resource Java 8

Optional is a class introduced in Java 8 and is based on the Standard Option/Some/None of the Scala language. In this article will not address the use of Optional with Lambdas and Streams Java 8. As we are talking about renormalization and compile-time safety, Optional class will be addressed on this light.

Anyone who program in Java has come across the famous NullPointerException exception. Even the creator of the concept of zero reference, Tony Hoare said that this was the "error of one billion dollars."

Consider the following code snippet:

public String getCarName() {// ....}

Seeing this method declaration, we can infer that the return type is always a string, right? Think again ...

Even in this simple, two types of return are possible: String or null. Yes, we always forget that any method in Java (which does not return a primitive type), returns an object or null.

And because of this simple assertion, we can never guarantee that any method call is null-safe without thoroughly analyzing their implementation or believe in their documentation (in the case of third-party API).

Listing 7. Possible null returns

List<Person> persons = repository.list();  
Address address = persons.get(0).getAddress();
System.out.println(address.getName());
....     

Yes, we have to analyze the code carefully and see if certain method returns null or not, as shown in Listing 7. If we are not sure if the method can actually return null, we must always use if (obj! = Null) to validate any return.

If a method can always return two types of data (null or a valid object), why not formalize this concept? For this we can use Optional. Independent Optional paper to reduce the verbosity and provide a more declarative, functional and clean style with flatMap/map/filter, the mere existence of this concept already promotes serious benefits.

Listing 8. Optional Use

 import java.util.Optional;
  import java.util.Random;
   
  public class Example {
      
      public static String getString1()  { return "OK"; }
      
      public static Optional<String> getString2()  { 
          
          String retorno;
          
          if(new Random().nextInt(2) == 0) {
              retorno = null;
          } else {
              retorno = "OK";
          }
          
          return Optional.ofNullable(retorno);
      }
      
      public static void main(String... args) {
          for(int i = 0; i < 10; i++) {
              Optional<String> option = getString2();            
              if(option.isPresent()) {
                  System.out.println(option.get());
              }
          }
      }
  }

What can we say about the two getString methods in Listing 8?

  • We can say with certainty that the first will always return a String;
  • The second can be a String or null, so the return is an Optional.

With the presence of Optional we can now clearly define when a method is null-safe (getString1()) and when it can return null(GetString2()). Notice when the idiomatic aspect is important and powerful. If a method can return null, we will refund an Optional <T> instead of T. If we know that the method never returns null, we can only return T.

This convention promotes the programmer, because it will not need to analyze the method of code to see if it is null-safe or not, just by seeing the return type, or method of input arguments (and in that an IDE is a great help).

In the case of GetString2(), as the user of the method knows that it returns Optional <T>, it probably will not forget to take the test with isPresent() method to see if the returned value is a T object or null . If a valid object, isPresent() == true, we can use the get method to get the object. Use get without testing with isPresent() is dangerous because it contains null Optional, we have an exception.

An alternative would be to use to get the code Listing 9.

Listing 9. Use OrElse

 public static void main(String... args) {
  for(int i = 0; i < 10; i++) {
      Optional<String> option = getString2();            
      System.out.println(option.orElse("NULL"));
  }
}

If Optional (a container) contains a valid object (in this case a String), it will be returned and printed. If it contains null, the string "NULL" is returned and printed.

The Optional class favors a more defensive style of programming, as explicitly warns the developer of a possible null return from a method (or when an argument passed to a method can be null or not), helping to minimize the presence of NullPointerException. Obviously, as the Optional was introduced in Java 8, most Java API classes do not make use of this concept, which somewhat limits its power (other than Scala, which has ample support Option in your library). View your code in Listing 10.

Listing 10. Optional class source code

 import java.util.function.Consumer;
  import java.util.function.Function;
  import java.util.function.Predicate;
  import java.util.function.Supplier;
   
  public final class Optional<T> {
   
      private static final Optional<?> EMPTY = new Optional<>();
   
      private final T value;
   
      private Optional() {
          this.value = null;
      }
   
      public static<T> Optional<T> empty() {
          @SuppressWarnings("unchecked")
          Optional<T> t = (Optional<T>) EMPTY;
          return t;
      }
   
      private Optional(T value) {
          this.value = Objects.requireNonNull(value);
      }
   
      public static <T> Optional<T> of(T value) {
          return new Optional<>(value);
      }
   
      public static <T> Optional<T> ofNullable(T value) {
          return value == null ? empty() : of(value);
      }
   
      public T get() {
          if (value == null) {
              throw new NoSuchElementException("No value present");
          }
          return value;
      }
   
      public boolean isPresent() {
          return value != null;
      }
   
      public void ifPresent(Consumer<? super T> consumer) {
          if (value != null)
              consumer.accept(value);
      }
   
      public Optional<T> filter(Predicate<? super T> predicate) {
          Objects.requireNonNull(predicate);
          if (!isPresent())
              return this;
          else
              return predicate.test(value) ? this : empty();
      }
   
      public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
          Objects.requireNonNull(mapper);
          if (!isPresent())
              return empty();
          else {
              return Optional.ofNullable(mapper.apply(value));
          }
      }
   
      public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
          Objects.requireNonNull(mapper);
          if (!isPresent())
              return empty();
          else {
              return Objects.requireNonNull(mapper.apply(value));
          }
      }
   
      public T orElse(T other) {
          return value != null ? value : other;
      }
   
      public T orElseGet(Supplier<? extends T> other) {
          return value != null ? value : other.get();
      }
   
      public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
          if (value != null) {
              return value;
          } else {
              throw exceptionSupplier.get();
          }
      }
   
      @Override
      public boolean equals(Object obj) {
          if (this == obj) {
              return true;
          }
   
          if (!(obj instanceof Optional)) {
              return false;
          }
   
          Optional<?> other = (Optional<?>) obj;
          return Objects.equals(value, other.value);
      }
   
      @Override
      public int hashCode() {
          return Objects.hashCode(value);
      }
   
      @Override
      public String toString() {
          return value != null
              ? String.format("Optional[%s]", value)
              : "Optional.empty";
      }
  }

Criteria API

Both in JPA 2.0 and Hibernate, we can write queries through Strings (HQL/JPQL) or using Criteria API. Again we had an impasse about oriented programming strings versus strongly typed. The biggest advantage of the Criteria API use is a compile-time error checking (if we are passing the correct class without spelling errors, etc.). And we will also be completely safe with respect to SQL injection.

The biggest drawback is the additional complexity in learning the Criteria API, at least initially, since HQL/JPQL are more intuitive for developers (as it probably deal with SQL for relational database). Another disadvantage is the verbosity, which considerably increases using Criteria API instead of HQL/JPQL.

Certainly, there are other techniques not mentioned in that article that "renormalize" and ensure compile-time safety, or an idiomatic style that indicates more clearly the programmer's intentions.

The "renormalize" given piece of code, impose fewer decisions a programmer must take, thereby reducing the possibility of errors.

The frameworks almost always meet this goal because it imposes a number of restrictions and steps to follow programmer, standardizing the collective knowledge to create a common language and increasing productivity as a whole. It would be chaos if each developer came with his/her own vision of how to implement persistence, MVC, etc.

Design patterns also favor the use restrictions. Good examples are the Template Method, Strategy, State, Command and Factories.

Of course, we can not forget the Generics. Despite being a resource considered complex, especially for beginners, to master its use is imperative question to make the compiler "his good friend".

Hugs and see you soon.

Links

Builder pattern

http://www.informit.com/articles/article.aspx?p=1216151&seqNum=2

Optional Oracle

http://www.oracle.com/technetwork/pt/articles/java/optional-java-se-8-2262752-ptb.html

Optional II

http://www.nurkiewicz.com/2013/08/optional-in-java-8-cheat-sheet.html



Web developer and passioned for web design, SEO and front end technologies.

What did you think of this post?
Services
[Close]
To have full access to this post (or download the associated files) you must have MrBool Credits.

  See the prices for this post in Mr.Bool Credits System below:

Individually – in this case the price for this post is US$ 0,00 (Buy it now)
in this case you will buy only this video by paying the full price with no discount.

Package of 10 credits - in this case the price for this post is US$ 0,00
This subscription is ideal if you want to download few videos. In this plan you will receive a discount of 50% in each video. Subscribe for this package!

Package of 50 credits – in this case the price for this post is US$ 0,00
This subscription is ideal if you want to download several videos. In this plan you will receive a discount of 83% in each video. Subscribe for this package!


> More info about MrBool Credits
[Close]
You must be logged to download.

Click here to login