DEV Community

Haidar
Haidar

Posted on

Control 2.0

This is my first article on Dev.to and i intend to keep it short. I will also need your feedback. I am a software engineer and i am lazy (in the sense that i do things which requires me to do less modifications later). I always code using the principles of ‘laziness’ in mind . All these is achievable using SOLID principles and proper object oriented techniques.
The topic is Reusing controls. So what are controls basically? In this article controls are validation/checks that are executed. For me instead of having a thousand ifs , it is proper to group them into their own logical unit. Typical use cases are as follows:

if (firstName.length() < 4){
     throw new IllegalArgumentException("Age cannot be less than 4")
}
if (lastName.length() < 4){
     throw new IllegalArgumentException("Age cannot be less than 4")
}
Enter fullscreen mode Exit fullscreen mode

...
And it goes like that. There is nothing wrong with this approach. But what happens if you want to reuse this in another context. Of course grouping those into static methods and including them in util classes like the following could be a good idea :

public static void validateName(String firstName, String lastName){
if (firstName.length() < 4){
     throw new IllegalArgumentException("Age cannot be less than 4")
}
if (lastName.length() < 4){
     throw new IllegalArgumentException("Age cannot be less than 4")
}
}

Enter fullscreen mode Exit fullscreen mode

But it lacks something? it lacks some structure ? what if you want certain checks to be optional ? then you would introduce a flag to check whether certain controls are activated or not. What if you want certain conditions to be activated only if it matches some pre-conditions -> MORE IFS ???
Below is the breakdown i propose :-
IControl- Base interface that contains the method control. The control method will be the one containing the actual checks/validation to be made.

@FunctionalInterface
public interface IControl {
void control() throws Exception;
}

Enter fullscreen mode Exit fullscreen mode

Preconditions- Preconditions are conditions that have to be met for some controls to happen. Preconditions can be used as Lambda expressions and chained using and() or or() operations.

import java.util.Objects;
public interface Precondition {
Boolean test();
default Precondition and(Precondition other) {
  Objects.requireNonNull(other);
  return () -> test() && other.test();
}
default Precondition negate() {
   return () -> !test();
}
default Precondition or (Precondition other) {
   Objects.requireNonNull(other);
   return ()-> test() || other.test();
}
}

Enter fullscreen mode Exit fullscreen mode

Control implementation - The control class has a list of preconditions and is an abstract class which implements the IControl interface. It has the control method as abstract and each control will need to extend the class and implement the control method.
The applyControl method will be the one to be called. It evaluates the preconditions using a reduce operation and check if the control is enabled using the enabled property. Properties can be enabled via this property statically and dynamically using the preconditions.


package com.example.controls;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public abstract class Control implements IControl {

 private List<Precondition> preconditions = new ArrayList<Precondition>();
private Boolean enabled = true;

 public Control(Boolean enabled) { 
  this.setEnabled(enabled);
 }

 public Control(List<Precondition> preconditions) {
  super();
  this.preconditions = preconditions;
 }

 public Control(Boolean enabled , List<Precondition> preconditions) {
  super();
  this.preconditions = preconditions;
  this.setEnabled(enabled);
 }

 public Control() {super();}
@Override
 public abstract void control() throws Exception;

 private Boolean reduce() {
    Precondition reducedPrecondition = preconditions.stream().reduce(()-> true, (f,s)-> f.and(s));
    return reducedPrecondition.test();
 }

 public void applyControl() throws Exception{
  if(this.enabled && reduce()) {
   control();
  }
 }

 public void addPrecondition(Precondition precondition) {
  preconditions.add(precondition);
 }
public Boolean getEnabled() {
  return this.enabled;
 }
public void setEnabled(Boolean enabled) {
  this.enabled= enabled;
 }
}

Enter fullscreen mode Exit fullscreen mode

Question : How are we going to use all these ?? Consider the following
Name Control- control/validation to check whether the name matches the required length.


public class NameControl extends Control {
private String enabled;
private User user
public NameControl(Boolean enabled ,User user, List<Precondition> preconditions) {
  super(enabled, preconditions);
  this.user = user;
}
@Override
public void control() throws Exception {
if (user.getLastName.length() < 3) {
 throw new Exception("Length of lastName should not be less than 3");
}
}
}

Enter fullscreen mode Exit fullscreen mode
  1. User Controls -groups a list of controls that belong to the user. In the example below, precondition is attached to the Name Control, that is controls are executed only if the user is a normal user. Using this approach, Name Control can be re used in a different context. Hence making controls like building blocks which can then be aggregated into single logical control units.
public class UserControl extends Control {
List<Control> userControls = new ArrayList<Control>();
public UserControl (Boolean enabled, User user){ 
    Precondition isNormalUser = ()-> !user.type.equals('Test'); 
    userControls.add(new NameControl(true,user, Arrays.asList(isNormalUser);
}
@Override
public void control() throws Exception {
     userControls.stream().forEach(Control::applyControl);
}
}

Enter fullscreen mode Exit fullscreen mode
  1. Using the controls- Controls can be declared and used simply as below. Moreover, controls can also be declared inline by extending the control class and overriding the abstract control method.
public static void main(String args[]){
   User user = new User("haidar","knightfury","Normal");
   new UserControl(true, user).applyControl();
}

Enter fullscreen mode Exit fullscreen mode

To conclude, this was a brief and simplified post about how to make controls reusable. There exists a lot of variations that i use about this, but basically it all comes down to this structure. For example Control can be further extended to ‘RepositoryControl’ to connect to a Repository to fetch the enabled status and other parameters (In this way, the fetching of enabled is abstracted away). Also the use of generics such that controls are applied only to a specific type. If you use the Spring Framework, each control can become a component so no object creation is necessary.
Thank you for reading, let me know if it is interesting, so i can have a part 2 for the different variations or i can share the code on a repository.

haidarknightfury

Top comments (0)