SWDES
Veröffentlicht auf SWDES (https://swdes.net)

Startseite > Projekte > Java projects > SafeR > SafeR - examples

SafeR - examples

How to use the SafeR framework

SafeR is a lightweight framework to apply a Design by Contract (DbC) coding style. Additionally, SafeR provides operations for checks which return a boolean value beside other related operations. SafeR supports DbC in two ways: with safe reference objects and assertions with just static operations. While the latter is the same approach as seen typically in "Assert" classes with static operations, the former is the preferred approach wherever possible. Safe reference objects can be seen as (immutable) containers or wrappers which assert that the references they contain are in the specified / expected state. By using the type of a safe reference class as parameter type or return type of an operation, the caller of the operation is forced to comply with the contract at compile time - and the declaration of the contract (preconditions, postconditions and invariants) in the Javadoc can be omitted. While the assertions with just static operations can detect programmatic issues very early and very close to the location of the issue, with safe references the most of the relevant programmatic issues can be prevented by the compiler already and the rest of the programmatic issues should be detected directly at the location of the issue.

In case a programmatic issue is detected by SafeR, a runtime exception or error will be generated with a large exception message containing all the details which might be useful to fix the issue, including a filtered stacktrace. The exception message can be customized easily, translated to any language and split into parts in order to extract information for the user. Any given object or value will be encoded to a detailed string automatically or customized for the exception message. Please see also the sample log. Optionally, an analyzer tool like the SWDES Code Analyzer can be used to generate an analysis report or an advice about the root cause into the exception message. The SWDES Code Analyzer will try to perform a backtracking using static bytecode analysis to find the origin of the issue. This can make the manual backtracking of an issue obsolete, in applications where the assertion is not as close to the location of the issue (due to a less intensive use of assertions).

SafeR can be and shall be active in production environments as well, to detect programmatic issues which have not been detected previously and to prevent invalid states. In order to prevent any issue for the business or the users, due to thrown runtime exceptions, SafeR provides convenient possibilities to unblock an application quickly and just within the production environment, but just temporarily until the programmatic issue has been fixed. This can save your day or even your job! SafeR prevents that assertions can be switched off permanently and forgotten easily, as it can happen for instance with the Java assertions provided by the JDK.

SafeR is designed to reduce its impact on the performance of an application as much as possible, so that no impact should be noticeable even if a very large application uses SafeR very intensively.

Hint: even if SafeR is extremely easy to use already, for the everyday work it might be useful to create editor templates within your IDE for the code and/or Javadocs to quickly create an assertion with just two or three key strokes.

In SafeR each contract (in terms of DbC) is represented by one safe reference class. For instance the contract that a given object must not be null is represented by the class NotNull, the contract that a given text must not be null or empty is represented by the class NotEmptyText, the contract that a given collection must not contain any null values is represented by the class NoNulls, and so on. Most of these safe reference classes provide plenty of operations related to their contract, to create a safe reference from the given object, to assert the object meets the contract, to check whether or not the object meets the contract, and so on. This design makes it easy to extend SafeR with a new contract by just adding another safe reference class.

Let's look at some usage examples. For instance to ensure that an object is not null we can write...

NotNull<Foobar> foobar = NotNull.of(foobarInput);

...to create a safe reference. Here the foobar object is a safe reference that contains the foobar input. If the foobar input is null, then SafeR will throw a runtime exception before the safe rerefence is created actually. Now, we can pass foobar to any subsequent operation (with its type NotNull<Foobar> as parameter type) and that subsequent operation can trust safely that the foobar input inside foobar is not null. Hence, all subsequent operations which use the foobar input do not need to apply another null check. The other way around: a safe reference type as parameter type in a method signature will effectively force all callers of that method to meet the contract already at compile time!

Or instead of a safe reference we just use a static operation to ensure that an object is not null like so:

NotNull.assertFor(foobarInput);

SafeR will throw a runtime exception if the foobar input is null, but the assertFor operation does not return a safe reference. Hence, we can just pass the original foobar input to any subsequent operation and all subsequent operations which use the foobar input will have to apply another null check. The the assertFor operations can be used as assertions of preconditions and postconditions, as well as assertions not related to DbC anywhere in the code.

 

 

To ensure a given text is not null or empty we can write...

NotEmptyText text = NotEmptyText.of(textInput);

...or...

NotEmptyText.assertFor(textInput);

Let's look at another example: a given collection must not contain any null values. To ensure this, we write...

NoNulls<Foobar> foobars = NoNulls.of(unsafeFoobars);

...or...

NoNulls.assertFor(unsafeFoobars);

As simple as that we can ensure a collection meets a contract of our choice. But in this example we have to pay more attention, because SafeR will create a copy of the collection passed to the of operation to ensure its referent is not referenced anywhere else and thus the collection cannot be changed after it has been passed to the of operation. This is fine for most cases. In case the collection is too large and therefore the performance is too poor at this point, a boolean flag can be passed to the of operation to skip the copy action:

NoNulls<Foobar> foobars = NoNulls.of(unsafeFoobars, true);

Please note: skipping the copy action requires that the caller of the of operation ensures that the collection passed to the of operation is not referenced anywhere else!

Sometimes we just want to query whether or not a given object meets a certain contract, e.g. to check if the unsafeFoobars contain a null value we can write...

if (NoNulls.isTrueFor(unsafeFoobars)) { ... }

...or...

NoNulls.ifTrueFor(unsafeFoobars, f -> { ... });

...or...

NoNulls.ifFalseFor(unsafeFoobars, f -> { ... });

...where we pass as second parameter a Consumer to process the foobars.

 

How do we use the safe references we created?

If we create a safe reference to pass it to a subsequent operation, sooner or later we have to access the referent contained in the safe reference. We can unwrap the referent like so...

Foobar safeFoobar = foobar.get();

...or so...

Foobar safeFoobar = foobar.getObject();

...or so...

Foobar safeFoobar = foobar.getReferent();

...or so...

Foobar safeFoobar = foobar.getValue();

...or so...

Foobar safeFoobar = foobar.ref();

...or so...

Foobar safeFoobar = foobar.safe();

...or so...

Foobar safeFoobar = foobar.value();

...and all of these accessors do the same, they return the referent. These sibling operations exist solely to support various coding styles. So it is up to you which accessor you use. It is recommended to define a coding standard for each project, where that coding standard tells the developers precisely where to use which accessor. For instance a project might use always the value accessor for simple values and the ref accessor for complex objects. Or a project might use always the safe accessor to highlight that a safe referent is returned. If such a coding standard is applied consistently, it improves the readability of the code.

 

 

Even in cases where we do not need to process the referent we will need to unwrap it at least at the point where we store the referent in an instance variable e.g. a field of an entity. For instance a setter might unwrap the referent and the corresponding getter might wrap the referent into a new safe reference in order to return it. Never type a field with a safe reference type! This is important, because a field may not be initialized or set to a null value, but the safe reference instance itself must never be null at no point in time! In addition, to assert that the safe reference instances itself are not null, we can write...

Safe.assertFor(foobar);

...like a precondition at the beginning of a method or, even better, by means of AOP or generated proxies we can test the parameters of a method in a central place and not visible in the business code. The use of AOP or generated proxies is recommended for this purpose, but in places where we cannot employ AOP or generated proxies we can still put that assertion at the beginning of a method. Since it is an obvious programmatic error and very unlikely to happen, that a safe reference instance itself is null, this assertion is not required but recommended. However, if it happens without that assertion, we just get a NullPointerException instead.

 

There is much more we can do with SafeR - discover it!

 

 

 

More coming soon.

 

 


 

About the creator & author

 

 

© Some copyrights reserved. Please find the copyrights information in the web page footer.

Quell-URL: https://swdes.net/de/content/safer-examples?page=3