We've seen examples where we 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:
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:
@Trait
annotation at class level, or with declared types such as the following:declare trait KidFriendlykidsAppeal: String end
@Traitable
annotation.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 our eShop 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 eShop 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.
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 KidFr
iendly
element.
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)
This syntax, and the use you can give to the traits, is also exemplified in TraitTest
of the code base.
3.133.156.107