Seite 1 von 1
Refactoring von tiefen Objektstruckturen
Verfasst: Donnerstag 19. November 2015, 20:48
von bennn
Hallo, ich wollte nach Anregungen fragen wie ich in meinen Code generell besser mit langen Objektketten umgehen kann. Nehmen wir beispielsweise:
Code: Alles auswählen
search_result.found_namelist_item.namelist_item_descript.const_block_idx
(Könnte ja durchaus noch länger sein) Das Programmieren wird immer unangenehmer, da sich lange Codezeilen bilden und die Codevervollständigung versagt auch, so dass bei den jeweiligen Objektattributen immer die Implementierung zu Rate gezogen werden muss. Die neu eingeführten type hints machen das etwas besser, aber optimal ist es immer noch nicht.
Außer die Zeile in Variabeln/Membern aufzusplitten, gibt es generell Anregungen wie ich meine Codestrucktur refactoren kann um diesen Effekt zu vermeiden?
Re: Refactoring von tiefen Objektstruckturen
Verfasst: Donnerstag 19. November 2015, 21:37
von __deets__
Re: Refactoring von tiefen Objektstruckturen
Verfasst: Donnerstag 19. November 2015, 22:57
von snafu
bennn hat geschrieben:Code: Alles auswählen
search_result.found_namelist_item.namelist_item_descript.const_block_idx
In einem gut strukturierten Programmcode hätte man solche Probleme nicht. Da wäre dann das `search_result` eine Liste mit Treffern. Der einzelne Treffer wäre ein NamedTupe oder ein Dictionary, in welchem die Bezeichnung, Detail, Pfad/URL/ID/was-auch-immer abgebildet werden.
Mit anderen Worten: Der beste Umgang mit tiefen Verschachtelungen ist deren Vermeidung. Wenn du Refactoring betrieben möchtest, dann überlege, wie du die Struktur flach halten kannst, ohne dass es unübersichtlich wird. In Fällen, wo sich tiefe Verschachtelungen nicht vermeiden lassen, kann es manchmal sinnvoll sein, gewisse "Abkürzungen" für häufig genutzte Pfade zu schaffen, indem ein Alias-Attribut oder ein Alias als Methode angeboten wird.
Re: Refactoring von tiefen Objektstruckturen
Verfasst: Freitag 20. November 2015, 09:50
von bennn
@ __deets__, snafu danke für die Hinweise! Ich find es gar nicht so einfach das immer durchzusetzen, da ich ja gleichzeitig sehr modularen Code schreiben möchte.
@snafu Wenn ich die Rückgabe der Suchergebnisse anpassen würde, so verschiebt sich die Verschachtelung doch nur um eine Ebene, oder? Auch wenn ich search_result anders organisiere, müssen ja zumindet in der Rückgabemethode die relevanten, verschachtelten Attribute zusammengesammelt werden
Re: Refactoring von tiefen Objektstruckturen
Verfasst: Freitag 20. November 2015, 11:41
von __deets__
Ja, irgendwer muss in die Innereien reinlangen - beliebig schoen wird das nicht. Aber es ist halt wichtig, dass das nach aussen hin ein sauberes Interface ist, das sich bedienen laesst ohne intim werden zu muessen.
Re: Refactoring von tiefen Objektstruckturen
Verfasst: Freitag 20. November 2015, 12:45
von snafu
@bennn:
Was wird denn genau in der Struktur abgebildet? Denn dies ist ja ausschlaggebend für die sinnvolle Organisation einer Struktur. Für manche Anwendungsgebiete ist eine tief verschachtelte Struktur durchaus hilfreich (z.B. bei einem
AST).
Es kann auch vorkommen, dass man für die Abbildung des "großen Ganzen" eine sehr detaillierte und tief verschachtelte Struktur verwendet (oder vorgegeben bekommt), aber für das, was man mit dieser Struktur vor hat, nur eine Teilmenge benötigt. Diese Teilmenge könnte man dann in eine flachere Struktur überführen, um für die nachfolgenden Schritte auf dieser vereinfachten Struktur zu arbeiten.
Re: Refactoring von tiefen Objektstruckturen
Verfasst: Freitag 20. November 2015, 22:42
von bennn
@snafu Es handelt sich um eine (Names)liste, mit welcher ein Quellcode (in dem Fall in der Sprache PL0) analysiert werden soll.
Diese Liste enthält Objekte vom Type
. Jedes ListItem enhält ein Objekt Grundtyp
.
Je nach Quellcode-Token wird ein Objekt
,
,
angelegt. Diese können dann jeweils wieder unterschiedliche Attribute haben an welche ich letztendlich ran kommen möchte
Danke schon mal für die Erklärungen bisher. Ich sehe mal wo ich das umsetzen kann.
Re: Refactoring von tiefen Objektstruckturen
Verfasst: Samstag 21. November 2015, 00:23
von pillmuncher
@bennn: Ich nehme an, du meinst die Sprache
PL/0.
Ein Klassiker zum Verarbeiten von ASTs ist das
Visitor Pattern:
Code: Alles auswählen
class ListItemDescriptionConst:
pass
class ListItemDescriptionVar:
pass
class ListItemDescriptionProcedure:
pass
class CodeVisitor:
def visit(self, node):
return getattr(self, 'visit_' + type(node).__name__)(node)
def visit_ListItemDescriptionConst(self, node):
print('found ListItemDescriptionConst', node)
def visit_ListItemDescriptionVar(self, node):
print('found ListItemDescriptionVar', node)
def visit_ListItemDescriptionProcedure(self, node):
print('found ListItemDescriptionProcedure', node)
def main():
visitor = CodeVisitor()
visitor.visit(ListItemDescriptionConst())
visitor.visit(ListItemDescriptionVar())
visitor.visit(ListItemDescriptionProcedure())
if __name__ == '__main__':
main()
Ergebnis:
Code: Alles auswählen
found ListItemDescriptionConst <__main__.ListItemDescriptionConst object at 0x7fb8e17a5e48>
found ListItemDescriptionVar <__main__.ListItemDescriptionVar object at 0x7fb8e17a5e48>
found ListItemDescriptionProcedure <__main__.ListItemDescriptionProcedure object at 0x7fb8e17a5e48>
Der Einfachkeit halber habe ich den Dispatch über den Typnamen ausgeführt, aber wenn der Knotentyp nicht als Klasse kodiert ist könnte man es auch anders lösen. Dazu müsstest du allerdings mehr Informationen liefern.
Das noch: die
visit() Methode kann man im Visitor rekursiv aufrufen und sich so am AST entlanghangeln. Sofern es zB. die Klassen
ProcedureParams und
ProcedureBody gibt, die die Parameter bzw. den Rumpf einer Prozedur beschreiben, ungefähr so:
Code: Alles auswählen
def visit_ListItemDescriptionProcedure(self, node):
name = node.name
params = self.visit(node.params)
body = self.visit(node.body)
return ...
def visit_ProcedureParams(self, node):
return ...
def visit_ProcedureBody(self, node):
return ...
Re: Refactoring von tiefen Objektstruckturen
Verfasst: Samstag 21. November 2015, 00:37
von BlackJack
@bennn: Also ich finde ja neben dem tiefen durchgreifen die Namen am gruseligsten, sowohl die Attributnamen als auch die Namen der Datentypen.
Re: Refactoring von tiefen Objektstruckturen
Verfasst: Samstag 21. November 2015, 23:35
von bennn
@BlackJack Dann mach dich erst auf meinen weiteren Code gefasst

Was könnte ich denn bei den Namen besser machen?
Danke für den Hinweis auf das Visitor Pattern. Ich versuch damit ein Codefragment zu verbessern, was wirklich nicht schön aussieht. Es ist so, dass auch die Sematik des PL/0 Code untersucht werden soll und wenn diese korrekt ist, Code generiert werden soll. Die Sematik des Codes wird graphenbasiert geprüft. Jetzt habe ich also eine Funktion, welche ein Token prüft.
Code: Alles auswählen
import logging
from parser.codegen.CodeGenSearch import SearchIdentResults, SearchResultEnum
import parser.codegen.CodeGeneration
class CodeVisitor:
def __init__(self, generator):
self.generator = generator
def visit(self, search_result: SearchIdentResults):
namelist_item = search_result.get_namelist_item()
return getattr(self, 'visit_' + type(namelist_item.namelist_item_descript).__name__)(search_result)
def visit_NamelistItemDesciptProc(self, search_result: SearchIdentResults):
pass
def visit_NamelistItemDescriptConst(self, search_result: SearchIdentResults):
pass
def visit_NamelistItemDescriptVar(self, search_result: SearchIdentResults):
pass
class CodeVisitorFactorIdent(CodeVisitor):
def visit_NamelistItemDesciptProc(self, search_result: SearchIdentResults):
logging.error('graph factor, semantic check 2: expexted var/const, found proced')
exit(-1)
def visit_NamelistItemDescriptConst(self, search_result: SearchIdentResults):
item = search_result.get_namelist_item()
const_idx = item.namelist_item_descript.const_block_idx
const_idx_endian = self.generator._short_to_little_endian(const_idx)
self.generator.code(parser.codegen.CodeGeneration.VMOperationEnum.puConst, const_idx_endian)
def visit_NamelistItemDesriptVar(self, search_result: SearchIdentResults):
if search_result.search_result_enum is SearchResultEnum.FOUND_GLOBAL:
self.generator._push_val_global_var(search_result)
elif search_result.search_result_enum is SearchResultEnum.FOUND_LOCAL:
rel_var_address = search_result.found_namelist_item.namelist_item_descript.relAdress
rel_var_address_endian = self.generator._short_to_little_endian(rel_var_address)
self.generator.code(parser.codegen.CodeGeneration.VMOperationEnum.puValVrLocl, rel_var_address_endian)
elif search_result.search_result_enum is SearchResultEnum.FOUND_MAIN:
rel_var_address = search_result.found_namelist_item.namelist_item_descript.relAdress
rel_var_address_endian = self.generator._short_to_little_endian(rel_var_address)
self.generator.code(parser.codegen.CodeGeneration.VMOperationEnum.puValVrMain, rel_var_address_endian)
if __name__ == '__main__':
def fac2_ident(self, search_result):
if search_result.search_result_enum is SearchResultEnum.NOT_FOUND:
logging.error('fa2: couldnt find ident ' + ident)
exit(-1)
code_visitor = CodeVisitorFactorIdent(self)
code_visitor.visit(search_result)
Re: Refactoring von tiefen Objektstruckturen
Verfasst: Sonntag 22. November 2015, 00:07
von BlackJack
@bennn: Stimmt der Code machts nicht besser. Das sieht für mich nicht nach Python aus. Das sind Java-Namen und statische Typisierung, nur halt ohne das es tatsächlich statisch typisiert ist.
Re: Refactoring von tiefen Objektstruckturen
Verfasst: Sonntag 22. November 2015, 13:31
von bennn
@BlackJack Wie könnte ich meine Namen besser wählen? Ich habe mich eigendlich an der PEP0008 orientiert. Und kannst du mir ein Beispiel geben was ich bei der typiesierung besser lösen könnte?
Re: Refactoring von tiefen Objektstruckturen
Verfasst: Sonntag 22. November 2015, 13:48
von BlackJack
@bennn: Die Namen sehen halt aus wie Java-Namen, beziehungsweise sogar so als wenn man sich über Java-Namen lustig machen wollte, viel zu lang und zu klobig. Das hat nichts mit der Schreibweise oder PEP8 zu tun sondern das sieht einfach total „over engineered“ aus. Der Verdacht wird durch die tiefe Objektstruktur noch verstärkt. Und durch die Typannotationen. Was man da besser machen könnte wäre sie einfach weg zu lassen.
Re: Refactoring von tiefen Objektstruckturen
Verfasst: Sonntag 22. November 2015, 16:50
von bennn
@BlackJack danke für die Hinweise. Es stimmt das ich bisher eher in Java entwickelt habe. Ich dachte es ist eine gute Angewohnheit so aussagekräftige Namen wie möglich zu verwenden, aber ich habe es wohl tatsächlich etwas übertrieben

Re: Refactoring von tiefen Objektstruckturen
Verfasst: Sonntag 22. November 2015, 17:57
von BlackJack
@bennn: Aussagekräftige Namen ja, aber sind sie das denn? Was soll zum Beispiel der `ListItem*`-Präfix? Dass Objekte von dem Typ vielleicht mal als Elemente in Listen stecken werden? Das wäre doch eine Information die gar nichts mit dem Datentyp zu tun hat.
Mich würde ja mal das Problem interessieren welches da gelöst werden soll und Deine Lösung, ob die nicht viel zu „javaesque“ ist.
Re: Refactoring von tiefen Objektstruckturen
Verfasst: Sonntag 22. November 2015, 23:12
von bennn
@BlackJack Hm, da hast du Recht, der ListItem ist vermutlich keine passende Bezeichnung. Es geht darum einen Compiler für die Sprache PL/0 zu schreiben, der Bytecode für eine (bereitgestellte) VM erzeugt.