Zuerst eine Anmerkung zur Terminologie und dann eine Verallgemeinerung.
Auf eine "globale Variable" kann überall in Ihrem Programm zugegriffen werden, daher ist der Geltungsbereich global. Die _variables
, auf die Sie in Ihrer Frage verweisen, sind private Felder im Rahmen Ihres Objekts. Auf sie kann nur durch Code zugegriffen werden, der in diesem Objekt definiert ist. Sie sind jedoch berechtigt, sich über die Anhäufung von privaten Arbeitsvariablen in Ihren Objekten Sorgen zu machen.
Das Entwerfen von Objekten ist schwierig und Techniken und Ideen haben sich über mehrere Jahrzehnte Praxis und Forschung entwickelt. Das von Michael Feathers eingeführte Akronym SOLID fasst fünf Prinzipien für objektorientiertes Design zusammen, die nützliche Kriterien für die Bewertung Ihres Designs liefern. Auch das Buch, Design Patterns: Elements of Reusable Object-Oriented Software, von Gamma et al. und erstmals im Jahr 1994 veröffentlicht, bietet eine gute Zusammenfassung und Kategorisierung von Designs in der objektorientierten Programmierung. Dieses Buch verwendet einen Dokumenteneditor als Fallstudie, um die Verwendung solcher Muster zu demonstrieren. Sowohl die SOLID-Prinzipien als auch die Entwurfsmuster im Buch sind Abstraktionen, sie sagen nicht, wie man ein Programm schreibt, aber sie geben eine Reihe von gemeinsamen Ideen, die Programmierer diskutieren und bewerten können. Daher werde ich beide Tools in meiner Antwort verwenden, aber in den letzten Jahren wurden zusätzliche Techniken entwickelt, um den Software-Entwicklungsprozess, insbesondere test driven development und , weiter zu verbessern.
Das S in SOLID steht für Single Responsibility Principle und ist ein guter Ausgangspunkt, um Ihr Beispiel zu betrachten. Wenn Sie Ihr Objekt Application
aufrufen und die privaten Arbeitsvariablen als globale Variablen betrachten, schlägt es vor, dass Sie die gesamte Anwendung in einem einzigen Objekt schreiben. Was Sie tun können, ist, Application
in eine Reihe von verschiedenen Objekten zu trennen, die sich mehr auf einen einzelnen Verantwortungsbereich konzentrieren. Zuerst dachte ich, ich würde das Application
Objekt umbenennen. Ich ging für EditorWindow
. In meinem Beispiel unter EditorWindow
hat auch eine Header
und eine DocumentView
.
Kompilieren Sie den Code unten mit:
valac -X -DGETTEXT_PACKAGE --pkg gtk+-3.0 text_editor_example.gs
Die Verwendung von -X -DGETTEXT_PACKAGE
ist am Ende dieser Antwort erklärt.
[indent=4]
uses
Gtk
init
Intl.setlocale()
Gtk.init(ref args)
var document = new Text("Lorem Ipsum")
var header = new Header("My text editor")
var body = new DocumentView(document)
var editor = new EditorWindow(header, body)
var document_selector = new DocumentFileSelector(editor)
var load_new_content_command = new Load(document, document_selector)
header.add_item(new OpenButton(load_new_content_command))
editor.show_all()
Gtk.main()
class EditorWindow:Window
construct(header:Header, body:DocumentView)
this.window_position = WindowPosition.CENTER
this.set_default_size(400, 400)
this.destroy.connect(Gtk.main_quit)
this.set_titlebar(header)
var box = new Box(Gtk.Orientation.VERTICAL, 1)
box.pack_start(body, true, true, 0)
this.add(box)
class Header:HeaderBar
construct(title:string = "")
this.show_close_button = true
this.set_title(title)
def add_item(item:Widget)
this.pack_start(item)
class OpenButton:ToolButton
construct(command:Command)
this.icon_widget = new Image.from_icon_name(
"document-open",
IconSize.SMALL_TOOLBAR
)
this.clicked.connect(command.execute)
class DocumentView:ScrolledWindow
construct(document:TextBuffer)
var view = new TextView.with_buffer(document)
view.set_wrap_mode(Gtk.WrapMode.WORD)
this.add(view)
interface Command:Object
def abstract execute()
interface DocumentSelector:Object
def abstract select():bool
def abstract get_document():string
class Text:TextBuffer
construct (initial:string = "")
this.text = initial
class DocumentFileSelector:Object implements DocumentSelector
_parent:Window
_uri:string = ""
construct(parent:Window)
_parent = parent
def select():bool
var dialog = new FileChooserDialog("Open file",
_parent,
FileChooserAction.OPEN,
dgettext("gtk30", "_OK"),
ResponseType.ACCEPT,
dgettext("gtk30", "_Cancel"),
ResponseType.CANCEL
)
selected:bool = false
var response = dialog.run()
case response
when ResponseType.ACCEPT
_uri = dialog.get_uri()
selected = true
dialog.destroy()
return selected
def get_document():string
return "Reading the text from a URI is not implemented\n%s".printf(_uri)
class Load:Object implements Command
_receiver:TextBuffer
_document_selector:DocumentSelector
construct(receiver:TextBuffer, document_selector:DocumentSelector)
_receiver = receiver
_document_selector = document_selector
def execute()
if _document_selector.select()
_receiver.text = _document_selector.get_document()
ein gemeinsames Muster auf hoher Ebene für grafische Benutzerschnittstellen ist model-view-controller (MVC). Es geht darum, Ihre Objekte zu entkoppeln, damit sie leicht wiederverwendet und geändert werden können. Im Beispiel document
wurde das Objekt, das das Modell darstellt. Wenn Sie dies zu einem separaten Objekt machen, können mehrere Ansichten derselben Daten angezeigt werden. Wenn Sie beispielsweise eine StackOverflow-Frage schreiben, haben Sie ein Editorfenster, aber auch eine Vorschau. Beide sind unterschiedliche Ansichten derselben Daten.
Im Beispiel wurde die Kopfleiste mit Hilfe der command pattern in verschiedene Objekte unterteilt. Jeder Schaltfläche in der Symbolleiste ist ein Befehl zugeordnet. Wenn die Befehle als separate Objekte vorliegen, kann der Befehl wiederverwendet werden. Zum Beispiel kann die Tastenkombination Strg-O auch den Befehl Load
verwenden. Auf diese Weise muss der Code für den Befehl, der an die Schaltfläche zum Öffnen des Dokuments angehängt ist, nicht neu geschrieben werden, um ihn an Ctrl-O anzuhängen.
Das Befehlsmuster verwendet eine Schnittstelle. Solange ein Objekt die Methode execute()
implementiert, kann es als Befehl verwendet werden. Der Befehl Load
verwendet auch eine Schnittstelle für das Objekt, das den Benutzer fragt, welche URI geöffnet werden soll. Gtk + bietet auch eine FileChooserNative. Wenn Sie also einen FileChooserNative
Dialog anstelle eines FileChooserDialog
verwenden möchten, müssten Sie nur ein neues Objekt schreiben, das die DocumentSelector
Schnittstelle implementiert und stattdessen an den Load
Befehl übergibt. Indem Sie Objekte auf diese Weise entkoppeln, wird Ihr Programm viel flexibler und die Verwendung von privaten Feldern wird auf jedes Objekt beschränkt.
Als Randnotiz gab es beim Zusammenstellen Ihres Beispiels ein paar Warnungen: warning: Gtk.Stock has been deprecated since 3.10
. Das Beispiel in dieser Antwort verwendet die neuere Art und Weise: „‚
- für die offene Dokumentsymbol the GNOME developer documentation for Stock Items Zustände‘Use namens Symbol‚Dokument-open‘oder die Bezeichnung“ _open Also habe ich
document-open
verwendet. Diese Namen stammen aus der freedesktop.org Icon Naming Specification
- für die OK-Schaltfläche in der Dateiauswahldialog der GNOME Developer documentation Staaten "Verwenden Sie kein Symbol. Verwenden Sie die Bezeichnung" _OK "." Der Unterstrich bedeutet, dass er internationalisiert und von übersetzt wird. verwendet 'Domänen', die Übersetzungsdateien sind. Für GTK + 3 heißt die Domäne
gtk30
. Um zu aktivieren, wenn Ihr Programm kompiliert wird, muss ein Makro für die Standarddomäne an den C-Compiler übergeben werden. Deshalb wird die -X -DGETTEXT_PACKAGE
benötigt. Auch im Genie-Programm wird Intl.setlocale()
benötigt, um das Gebietsschema auf die Laufzeitumgebung einzustellen. Wenn dies erfolgt mit so etwas wie LC_ALL="zh_CN" ./text_editor_example
Ihr Programm auszuführen wird die OK-Taste in der chinesischen zeigen, wenn Sie, dass locale
installiert haben
@AIThomas dies den Code soo viel mehr deutlich macht, dass ich bei Github gelesen haben! Du hast so eine großartige Synthese gemacht! Vielen Dank! –