We can modify the working memory whenever we detect that a specific condition is happening. We might remove objects, modify existing ones, and even delete them to trigger other rule executions. We’ve also seen that, sometimes, these elements make sense mostly within the rule executions and used declared types for these cases.
Whether we use declared types of external classes, most of the cases imply one of the following two strategies:
- Add new objects (declared types) to the working memory, representing some inference about our domain model
- Modify attributes of existing objects in the working memory, adding the inferred data to these properties
These strategies have a few disadvantages when it comes to decorating an existing model. The first case (adding new objects) might imply having to keep a reference between the domain model object and the new inferred object in some form. The second case (modifying attributes) might imply modifying the domain model when we might not wish to do so.
There is a third alternative to these strategies, which implies adding a new nature to already existing objects without having to create extra attributes in the original beans. This dynamic decoration of existing objects in the working memory is known as traits.
Traits are like adjectives from an object-oriented perspective. We can say a house is pretty or a car is pretty. Adjectives can apply to many different types. However, these types don’t need to share a common structure. This means that traits act like a flag that we can apply to multiple types and have specific attributes that apply to the adjective itself, and therefore, to the beans that apply the trait.
To explain this in a simpler way, think of traits as extra characteristics that we can dynamically add to certain objects in the working memory. We can filter these objects using these characteristics later in other rules. To do so, we need to do the following two things:
Define a trait:
declare trait KidFriendly kidsAppeal: String //traits may have attributes like any type end
Mark declared types that will have the trait applied with the
declare TraitableItem @Traitable name: String ... end
Once we follow these steps, we can define the rules that apply the trait to the traitable objects. Let’s consider an example based on some shop case. Consider that we want to start classifying specific elements as kid-friendly in order to add advertising to them based on an age tier.
We can have items that are kid friendly, such as colored paper, toys, or special clothes. We might also have kid-friendly providers (they provide us with a lot of school-related items) or kid-friendly sales channels (such as parent-based sites where our shop placed offers). If it wasn’t for this qualification as kid-friendly, these elements wouldn’t have any common structure. This is a good situation to apply traits.
Before we start using these traits, we need to see how to apply traits to our objects. To do so, we’ll see the syntax of the don keyword.
Adding traits with the
Whenever we have an object where we want to apply a trait, we can do so using the don keyword. It receives the traitable object first, the trait type second, and an optional third boolean parameter to decide whether it should be logically inserted in the working memory. It returns an object casted to the type of the trait. Let’s see the following example of its use:
rule "toy items are kid friendly" no-loop when $i: TraitableItem(name contains "toy") then KidFriendly kf = don($i, KidFriendly.class); kf.setKidAppeal("can play with it"); end
The previous rule defines the conditions where we would consider a
TraitableItem object (an object similar to
Item, except it is annotated with
@Traitable) as being kid-friendly. The reason for the
no-loop attribute on the rule is that
don is not creating a new element, it is only decorating an existing one in the working memory. As this decoration doesn’t make the object stop fulfilling the rule condition, the
no-loop avoids re-evaluations.
After using the
don keyword, we will be able to treat this object as a kid-friendly object in any other rule. This means that rules that filter objects by the
KidFriendly type can treat the traited object as a
Removing traits with the
If, at some point, after applying a trait to an object, we decide that this trait needs to be removed, we can do so with the
shed keyword. Shed will cause the deletion of the trait corresponding to the given argument type, as follows:
Object o = shed( $traitedObject, KidFriendly.class)