Πέμπτη 4 Απριλίου 2019

Chain of Responsibility design pattern

Today I have a OOP design pattern, the Chain of Responsibility. There are two programs, one use weak references (pointers to named groups), so we have three loggers connected, and the second use real pointers. The two examples report the same messages:


Writing to console: Entering function ProcessOrder().
Writing to console: Order record retrieved.
Writing to console: Customer Address details missing in Branch DataBase.
Writing to Log File: Customer Address details missing in Branch DataBase.
Writing to console: Customer Address details missing in Organization DataBase.
Writing to Log File: Customer Address details missing in Organization DataBase.
Writing to console: Unable to Process Order ORD1 Dated D1 For Customer C1.
Sending via email: Unable to Process Order ORD1 Dated D1 For Customer C1.
Writing to console: Order Dispatched.
Sending via email: Order Dispatched.


You can open the M2000 Interpreter, and write to console Edit A then press Enter and paste the code of the first example, then press Esc and write A and press Enter to execute it. Then do the same for B module, and last use Save chainof to save it as chainof.gsb for using it later (start M2000 Interpreter and write Load chainof to get it back).

Explain of constructor of ConsoleLogger. We have to use the same name as class name for a module. It is a module but is called as a function ConsoleLogger(). All arguments are in stack of values so because we want to extend this class we have to call Logger() passing the current stack. The [] identifier get the pointer of stack of values (is an object) and leave an empty stack. The ! before a stack object empty the stack by merging all values to callee function Logger(). So with this trick we pass the arguments to Logger() without using variables. The returned group is a float group and this merged to This object. Because we use Module Final WriteMessage {} no merging done for this module.
Module ConsoleLogger {
This=Logger(![])
}




\\ Chain of Responsibility design pattern 
\\ a class: part in Class Function removed from object after first copy (when copied from Class function)
\\ So constructors are not part of Logger Group (Group is the type of a user object in M2000)
Form 80,50
Global enum LogLevel    {
                    None = 0,
                    Info = 1, 
                    Debug = 2, 
                    Warning = 4,
                    Error = 8,   
                    FunctionalMessage = 16, 
                    FunctionalError = 32, 
                    All = 63              
             }
Class Logger {
Private:
 logMask=0&
 Group NextLogger
Public:
 Module Final Message(msg$, severity) {
  if not Valid(.NextLogger=>Null) then
   .NextLogger=>Message msg$, severity
  end if
  if Binary.And(.logMask ,severity)<>0 then
   .WriteMessage msg$
  End if
 }
 Module WriteMessage (m$) {
  Print m$
 }
 Module Final SetNext ( p as pointer) {
  .NextLogger<=p
 }
 Function Final Dispose {
  x=valid(.NextLogger=>Dispose())
  Clear .NextLogger
 }
Class:
 Module Logger (z) {
  .logMask<=z
  class EmptyLogger {
   Null
  }
  .NextLogger<=Pointer(EmptyLogger())
 }
}
Class ConsoleLogger {
 Module  Final WriteMessage (m$) {
  Print "Writing to console: " + m$
 }
class:
 Module ConsoleLogger {
  This=Logger(![])
 }
}
Class EmailLogger {
 Module  Final WriteMessage (m$) {
  Print "Sending via email: " + m$
 }
class:
 Module EmailLogger {
  This=Logger(![])
 }
}
Class FileLogger {
 Module  Final WriteMessage (m$) {
  Print "Writing to Log File: " + m$
 }
class:
 Module FileLogger {
  This=Logger(![])
 }
}
A=ConsoleLogger(All)
B=EmailLogger(FunctionalMessage+FunctionalError)
C=FileLogger(Warning+Error)
C.SetNext Pointer(B)
B.SetNext Pointer(A)


C.Message "Entering function ProcessOrder().", Debug
C.Message "Order record retrieved.", Info
C.Message "Customer Address details missing in Branch DataBase.", Warning
C.Message "Customer Address details missing in Organization DataBase.", Error
C.Message "Unable to Process Order ORD1 Dated D1 For Customer C1.", FunctionalError
C.Message "Order Dispatched.",FunctionalMessage
Call C.Dispose()  ' no need we use weak refrences (automatic created if poiner produced for named groups)







Write Edit B in M2000 console press enter and copy this, then press Esc to exit to console.

\\ Chain of Responsibility design pattern  2
\\ example from https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern
\\ This example use real pointers to groups
\\ so we have one group, the logger and two groups inside it in a chain
Form 80,50
Global enum LogLevel    {
                    None = 0,
                    Info = 1, 
                    Debug = 2, 
                    Warning = 4,
                    Error = 8,   
                    FunctionalMessage = 16, 
                    FunctionalError = 32, 
                    All = 63              
             }
Class Logger {
Private:
 logMask=0&
 Group NextLogger
Public:
 Module Final Message(msg$, severity) {
  if Binary.And(.logMask ,severity)<>0 then
   .WriteMessage msg$
  End if
  if not Valid(.NextLogger=>Null) then
   .NextLogger=>Message msg$, severity
  end if
 }
 Module WriteMessage (m$) {
  Print m$
 }
 Module Final SetNext ( p as pointer) {
  if  valid(.NextLogger=>Null) then
   .NextLogger<=p
  else
   .NextLogger=>SetNext p
  end if
 }
 Function Final Dispose {
  x=valid(.NextLogger=>Dispose())
   Clear .NextLlogger
 }
Class:
 Module Logger (z) {
  .logMask<=z
  class EmptyLogger {
   Null
  }
  .NextLogger<=Pointer(EmptyLogger())
 }
}
Class ConsoleLogger {
 Module  Final WriteMessage (m$) {
  Print "Writing to console: " + m$
 }
class:
 Module ConsoleLogger {
  This=Logger(![])    '  ![] pass current stack of values to function Logger()
 }
}
Class EmailLogger {
 Module  Final WriteMessage (m$) {
  Print "Sending via email: " + m$
 }
class:
 Module EmailLogger {
  This=Logger(![])
 }
}
Class FileLogger {
 Module  Final WriteMessage (m$) {
  Print "Writing to Log File: " + m$
 }
class:
 Module FileLogger {
  This=Logger(![])
 }
}
Logger->ConsoleLogger(All)
Logger=>SetNext Pointer(EmailLogger(FunctionalMessage+FunctionalError)) Logger=>SetNext Pointer(FileLogger(Warning+Error))
Logger=>Message "Entering function ProcessOrder().", Debug \\ better to use a For object {} structure For Logger { .Message "Order record retrieved.", Info .Message "Customer Address details missing in Branch DataBase.", Warning .Message "Customer Address details missing in Organization DataBase.", Error .Message "Unable to Process Order ORD1 Dated D1 For Customer C1.", FunctionalError \\ we can use Logger=>Message also Logger=>Message "Order Dispatched.",FunctionalMessage call .Dispose() } Logger->0& ' or Clear Logger

Δεν υπάρχουν σχόλια:

Δημοσίευση σχολίου

You can feel free to write any suggestion, or idea on the subject.