In object-oriented programming a class is a template which describes the common characteristics or attributes of objects. The word template is used in the sense of a tool that is used to build objects having attributes. Each class is an abstraction of a real-worls system or some other logical system that we are trying to model.
In order to define a class, you must specify one or more parent classes(called also as a superclass) of the class to be defined. A subclass inherits attributes from one or more superclasses. Classes that inherits builds a trees of classes:
<code lisp> CLIPS> (defclass animal (is-a OBJECT)) CLIPS> (defclass mammal (is-a animal)) CLIPS> (defclass bird (is-a animal)) CLIPS> (defclass reptile (is-a animal)) CLIPS> (defclass fish (is-a animal)) CLIPS> (defclass eagle (is-a bird)) CLIPS> (defclass dog (is-a mammal)) CLIPS> (defclass snake (is-a reptile)) CLIPS> (browse-classes animal) animal mammal dog bird eagle reptile snake fish
An instance is an object of a class that has values for the attributes.
An object's behavior is defined by its message-handlers(handlers). A message-handler for an object responds to messages and performs the required actions.
The general form of defclass:
(defclass <class-name> (is-a <superclasses> ) (slot 1) (slot 2) ... (slot N))
For example defclass of a person which store full name, age and sex of a person:
CLIPS> (defclass person (is-a USER) (multislot full-name) (slot age) (slot sex (allowed-symbols male female))) CLIPS> (make-instance John of person (full-name John Kowalsky) (age 44) (sex male))) [John] CLIPS> (make-instance Barbara of person (full-name Barbara Nowak) (age 23) (sex female))) [Barbara] CLIPS> (instances) [John] of person [Barbara] of person For a total of 2 instances. CLIPS> (unmake-instance John) TRUE CLIPS> (instances) [Barbara] of person For a total of 1 instance.
make-instance makes and instances of objects. The basic syntax is as follows:
(make-instance [<instance-name>] of <class> <slots-values>)
unmake-instances deletes the instance with specified name. (unmake-instance *) will remove all instances.
It is possible to define more than one instance of object at time - it's usage is similiar s in deffacts:
CLIPS> (definstances persons (Joey of person (full-name Joey Joe)(age 34) (sex male)) (Amanda of person (full-name Amanda Anderson) (age 45) (sex female))) CLIPS> (instances) CLIPS> (reset) CLIPS> (instances) [initial-object] of INITIAL-OBJECT [Joey] of person [Amanda] of person For a total of 3 instances.
What is important, that ony one instance of the same name may be used in a module, and a class cannot be redefined if instances of the class exist.
The one proper way to communicate with instances is to send a message. (send) message works anly with target object which has an appropriate handler. CLIPS automatically provides handlers:
If you define classes which do not inherit from USER class(such as subclass of INTEGER), you must also create appropriate handlers to carry out all desired tasks such as printing, creating or deleting instances. It is much easier to define subclasses of USER.
To get value from the instance use get-xx function of send, to put another value to the slot use put-xx:
CLIPS> (send [Barbara] get-full-name) (Barbara Nowak) CLIPS> (send [Barbara] put-full-name Barbara Kowalsky) (Barbara Kowalsky) CLIPS> (send [Barbara] print) [Barbara] of person (full-name Barbara Kowalsky) (age 23) (sex female)
To modify more than one slot of instance it is needed to use modify-instance function;
CLIPS> (modify-instance [Barbara] (full-name Basia Kowalsky) (age 24) (sex female)) TRUE CLIPS> (send [Barbara] print) [Barbara] of person (full-name Basia Kowalsky) (age 24) (sex female)
CLIPS allows you to define your own message-handlers(see also polymorphism). The general format of a message-handler is as follows:
(defmessage-handler <class-name> <message-name> [handler type] [optional-comment] (<parameters>* [wildcard-parameter]) <action>*)
Note, that only value of the last action is returned.
Handler-type tell when to execute the handler. the handler types are listed in the order that they are normally called during execution of message:
Simple message-handler to add values of two NUMBER objects:
(defmessage-handler NUMBER + (?arg) (+ ?self ?arg))
Now we are able to send message +:
CLIPS> (send 3 + 11) 14
?self is a special variable in which CLIPS stores the active instance. It is used to get values from slots using : operator. Go get slot of current instance just type ?selfL<slot-name> just like in an example below:
CLIPS> (defclass person (is-a USER) (multislot full-name) (slot age (type NUMBER)) (slot sex)) CLIPS> (make-instance Amy of person (full-name Amy Johnson) (age 35) (sex female)) [Amy] CLIPS> (make-instance John of person (full-name John Donatello) (age 20) (sex male)) [John] CLIPS> (defmessage-handler person write-age () (printout t "The age of " ?self:full-name " is " ?self:age crlf)) CLIPS> (send [Amy] write-age) The age of (Amy Johnson) is 35 CLIPS> (send [John] write-age) The age of (John Donatello) is 20
self parameter allow you to read a slot value. To write a slot value use the bind function. The ?self: notation is more efficient that sending messages but can only be used from within a handler on its active instance. The alternative way is to use dynamic-get and dynamic-put:
CLIPS> (defmessage-handler person write-age-2 () (printout t "The age of " (dynamic-get full-name) " is " (dynamic-get age) crlf)) CLIPS> (send [Amy] write-age-2) The age of (Amy Johnson) is 35
The ?self:age can only be used in a class and its subclasses which inherit the slot age. It is evaluated in static way - if a subclass redefines a slot, a superclass message-handler will fail if it tries to directly access the slot using ?self:<slot-name>. dynamic-get and dynamic-put check slots dynamically.
CLIPS> (defmessage-handler person change-name-to ($?new-name) (bind ?self:full-name ?new-name)) CLIPS> (send [Amy] change-name-to Amy Whitehouse) (Amy Whitehouse)