Иерархическая структура объекта классов с Incr Tcl

Я пытаюсь реализовать иерархическую структуру классов/подклассов объектов, например:

|-- Class1                  # mainClass
|   |-- SubClassA           # subClass
|   `-- SubClassB           # subClass
`-- Class2                  # mainClass
    |-- SubClassA           # subClass
    `-- SubClassB           # subClass

Главное здесь — иметь возможность объявлять разные подклассы, имеющие одно и то же имя в каждом основном классе (и иметь независимые переменные).

(Примечание: здесь я говорю об иерархии классов объектов, созданных во время выполнения, а не о наследии разных классов.)

Совершенно не знакомый с некоторыми расширенными аспектами, характерными для Tcl (пространство имен, область действия...), я попробовал следующий код:

package require Itcl

itcl::class subClass {
  variable InternalVariable
  constructor {} {
    puts "($this)     Current namespace      : [namespace current]"
    puts "($this)     InternalVariable scope : [itcl::scope InternalVariable]"
  }
}

itcl::class mainClass {
  variable SubClassesList
  constructor {} {
    set SubClassesList {}
    puts "($this)     current namespace      : [namespace current]"
    puts "($this)     SubClassesList scope   : [itcl::scope SubClassesList]"
  }
  method newSubClass {argName} {
    lappend SubClassesList [subClass $argName]
    puts "($this)     SubClassesList         : {$SubClassesList}"
  }
}

# Create the two main classes
mainClass Class1
mainClass Class2

# Add some subclasses to Class1 and Class2
Class1 newSubClass SubClassA
Class1 newSubClass SubClassB
Class2 newSubClass SubClassC
Class2 newSubClass SubClassB

который выдает ошибку при создании второго вхождения SubClassB:

(::Class1)     current namespace      : ::mainClass
(::Class1)     SubClassesList scope   : @itcl ::Class1 ::mainClass::SubClassesList
(::Class2)     current namespace      : ::mainClass
(::Class2)     SubClassesList scope   : @itcl ::Class2 ::mainClass::SubClassesList
(::mainClass::SubClassA)     Current namespace      : ::subClass
(::mainClass::SubClassA)     InternalVariable scope : @itcl ::mainClass::SubClassA ::subClass::InternalVariable
(::Class1)     SubClassesList         : {SubClassA}
(::mainClass::SubClassB)     Current namespace      : ::subClass
(::mainClass::SubClassB)     InternalVariable scope : @itcl ::mainClass::SubClassB ::subClass::InternalVariable
(::Class1)     SubClassesList         : {SubClassA SubClassB}
(::mainClass::SubClassC)     Current namespace      : ::subClass
(::mainClass::SubClassC)     InternalVariable scope : @itcl ::mainClass::SubClassC ::subClass::InternalVariable
(::Class2)     SubClassesList         : {SubClassC}
command "SubClassB" already exists in namespace "::mainClass"

Я, вероятно, упускаю момент о пространствах имен классов, потому что я не понимаю, как переменная SubClassesList может иметь две разные области, но «одно и то же» пространство имен/имя (из вывода отладки).

Я попытался создать новое пространство имен в методе newSubClass, но это не решило проблему и/или добавило некоторые неразрешимые ошибки пространств имен переменных...

method newSubClass {argName} {
  set SubClassName "[namespace current]::[namespace tail $this]"
  puts "($this)     SubClassName           : $SubClassName"
  namespace eval $SubClassName "lappend SubClassesList [subClass $argName]"
  puts "($this)     SubClassesList         : {$SubClassesList}"
}

Любая идея выполнить такую ​​​​вещь?

P-S: я использовал [incr Tcl] для реализации классов в своем проекте из соображений совместимости с существующей средой, но если кто-то считает, что другая реализация OO будет лучше/проще, дайте мне знать...


РЕДАКТИРОВАТЬ :

Нашел решение, используя namespace eval + namespace inscope для создания subClass объектов в новом пространстве имен, соответствующем имени mainClass объектов:

itcl::class mainClass {
  variable SubClassesList
  constructor {} {
    set SubClassesList {}
    puts "($this)     current namespace      : [namespace current]"
    puts "($this)     SubClassesList scope   : [itcl::scope SubClassesList]"
    # Create a new namespace corresponding to class name
    namespace eval $this {}
  }
  method newSubClass {argName} {
    # Create the subClass object in the $this namespace
    lappend SubClassesList [namespace inscope $this subClass $argName]
    puts "($this)     SubClassesList         : {$SubClassesList}"
  }
}

person Mousstix    schedule 09.07.2013    source источник
comment
Если вам нужна портативность, я предлагаю использовать snit или stooop. Оба они написаны на чистом Tcl и должны работать везде, где работает Tcl.   -  person Johannes Kuhn    schedule 10.07.2013


Ответы (1)


Я думаю, вы путаетесь между классом и объектом: класс — это схема или шаблон для создания объектов. В вашем примере:

  • mainClass и subClass — это классы
  • Class1, Class2, SubClassA, SubClassB и SubClassC — это объекты, также известные как экземпляры классов.

В Itcl каждый класс имеет свое собственное пространство имен, например, класс mainClass владеет пространством имен под названием ::mainClass — вы видите это доказательство в выводе вашего кода:

(::Class1)     current namespace      : ::mainClass
^^^^^^^^^^

Кроме того, у каждого объекта есть собственное пространство имен: ::::. Например, объект SubClassB владеет пространством имен ::mainClass:SubClass:

(::mainClass::SubClassB)     InternalVariable scope : @itcl ::mainClass::SubClassB ::subClass::InternalVariable
^^^^^^^^^^^^^^^^^^^^^^^^

Это означает, что у вас не может быть двух объектов с одинаковым именем — это ошибка, которую вы получаете. Если вы все еще хотите, чтобы пример работал, используйте #auto для имени объекта:

method newSubClass {argName} {
    lappend SubClassesList [subClass #auto] ;# <=== Use automatic naming
    puts "($this)     SubClassesList         : {$SubClassesList}"
}
person Hai Vu    schedule 10.07.2013
comment
Спасибо за ответ. Думаю, я понимаю суть и проблему. - person Mousstix; 10.07.2013
comment
Решение #auto работает, но является проблемой, потому что я должен знать имена каждого объекта subClass перед выполнением (слишком долго объяснять, почему здесь...). Тогда вопрос: возможно ли создать объект SubClassA в новом пространстве имен, например ::Class1, и как? - person Mousstix; 10.07.2013
comment
ОК, нашел альтернативное решение с namespace eval + namespace inscope (сообщение отредактировано). Не знаю, очень ли это чистое решение, но, похоже, оно соответствует моим потребностям. Еще раз спасибо за ваши объяснения. - person Mousstix; 10.07.2013