Я написал этот код (ниже), пытаясь узнать больше о классах примесей TclOO.
а. есть ли более прямой способ написать имя конкретного класса из конструктора миксина?
set concls [lindex [самостоятельный вызов] 0 end end-1]
б. есть ли способ упростить класс миксина, используя self
, не требующий дополнительных вызовов (например, init
), кроме mixin
(как показано ниже) при его использовании? (перепроверено с TclOO: миксин регистратора классов, не ясно)
миксин ::demo::utils::LoggingMixin
package require TclOO
package require try
package require logger
namespace eval ::demo::utils {}
namespace eval ::demo::naming {}
::oo::class create ::demo::utils::LoggingMixin {
variable log
constructor {args} {
set concls [lindex [self call] 0 end end-1]
try {
set log [::logger::servicecmd $concls]
} trap {LOGGER NO_SUCH_SERVICE} {} {
set log [::logger::init $concls]
}
# check for next
if {[llength [self next]] > 0} {
next {*}$args
}
}
}
::oo::class create ::demo::naming::GenericNaming {
mixin ::demo::utils::LoggingMixin
variable log
constructor {args} {
${log}::info "hello world"
}
}
::oo::class create ::demo::naming::NewNaming {
mixin ::demo::utils::LoggingMixin
variable log
constructor {args} {
${log}::info "new world"
}
}
::demo::naming::GenericNaming new
::demo::naming::NewNaming new
ожидаемый результат:
$ tclsh ./mixin.tcl
[Sun Jun 04 21:39:22 BST 2023] [::demo::naming::GenericNaming] [info] 'hello world'
[Sun Jun 04 21:39:22 BST 2023] [::demo::naming::NewNaming] [info] 'new world'
Чтобы получить класс создаваемого объекта, достаточно сделать это в конструкторе:
set concls [info object class [self]]
Вам не нужно перебирать аргументы (хорошо, так как это более подвержено ошибкам, когда есть сложные переадресации или new
и/или create
были переопределены).
Tcl 8.6 не требует от вас package require TclOO
или package require try
; в наши дни обе функции являются встроенными.
Я был бы склонен настроить миксин ведения журнала следующим образом:
oo::class create ::demo::utils::LoggingMixin {
variable log
constructor {args} {
set concls [info object class [self]]
try {
set log [::logger::servicecmd $concls]
} trap {LOGGER NO_SUCH_SERVICE} {} {
set log [::logger::init $concls]
}
next {*}$args
}
# TODO: Add methods for other wanted logging levels
method LogInfo args {
tailcall ${log}::info {*}$args
}
}
Потому что тогда подкласс может быть просто таким:
oo::class create LoggingDemo {
mixin ::demo::utils::LoggingMixin
constructor {args} {
my LogInfo "new world"
}
}
Или я мог бы даже сделать некоторые процедуры для дальнейшего улучшения синтаксиса:
constructor {args} {
set concls [info object class [self]]
try {
set log [::logger::servicecmd $concls]
} trap {LOGGER NO_SUCH_SERVICE} {} {
set log [::logger::init $concls]
}
proc logInfo args {
variable log; # Need to be explicit with this
tailcall ${log}::info {*}$args
}
next {*}$args
}
Другой подход заключается в том, чтобы миксин предоставлял самоинициализирующиеся методы ведения журнала:
oo::class create ::demo::utils::LoggingMixin {
variable LoggingMixinLog
method GetLog {level} {
if {![info exist LoggingMixinLog]} {
set concls [info object class [self]]
try {
set LoggingMixinLog [::logger::servicecmd $concls]
} trap {LOGGER NO_SUCH_SERVICE} {} {
set LoggingMixinLog [::logger::init $concls]
}
}
return ${LoggingMixinLog}::$level
}
method LogInfo args {
tailcall [my GetLog info] {*}$args
}
}
oo::class create LoggingDemo {
mixin ::demo::utils::LoggingMixin
constructor {args} {
my LogInfo "new world"
}
}
Это имеет то преимущество, что поддерживает смешивание с объектами или классами постфактум.
Если вы создаете обертки журналов, рекомендуется вызывать их с помощью tailcall
или uplevel 1
, чтобы текущий кадр стека был тем, который хочет сделать запись в журнале, а не маленьким вспомогательным методом/процедурой прокси.
для создания тега tcl-8.5 требуется 1500 реп, которых даже после многих лет бездействия у меня нет!
на данный момент я ограничен TCL 8.5 (сторонняя упаковка) и TCLOO, который я скомпилировал - я не видел тега для этого. Мне любопытно узнать, почему было бы полезно иметь однострочные функции-оболочки поверх более обычного документированного интерфейса
${log}::info ...
?