2016-04-10 12 views
0

Unten ist ein funktionierender Code einer ToolbarButton in Genie. Das Ziel besteht darin, die uri für die ausgewählte Datei zu holen und sie an das Konstrukt/init der Klasse zurückzugeben. Das Problem ist, dass ich in allen Beispielen globale Variablen gefunden habe (wie im folgenden Code gezeigt). Es sieht nicht intuitiv aus und ich befürchte, dass es immer schwieriger wird, Bugs zu entfernen, wenn der Code größer wird, da sich diese Variablen anhäufen. Gibt eine andere Möglichkeit, die Funktion openfile zu erstellen, die uri auf eine reguläre Variable innerhalb des Konstrukts/init der Klasse zurück? HierVermeiden von globalen Variablen in Genie

ist der Code:

uses 
    Granite.Widgets 
    Gtk 

init 
    Gtk.init (ref args) 

    var app = new Application() 
    app.show_all() 
    Gtk.main() 

// This class holds all the elements from the GUI 
class Application : Gtk.Window 

    _view:Gtk.TextView 
    _uri:string 

    construct() 

     // Prepare Gtk.Window: 
     this.window_position = Gtk.WindowPosition.CENTER 
     this.destroy.connect (Gtk.main_quit) 
     this.set_default_size (400, 400) 


     // Headerbar definition 
     headerbar:Gtk.HeaderBar = new Gtk.HeaderBar() 
     headerbar.show_close_button = true 
     headerbar.set_title("My text editor") 

     // Headerbar buttons 
     open_button:Gtk.ToolButton = new ToolButton.from_stock(Stock.OPEN) 
     open_button.clicked.connect (openfile) 

     // Add everything to the toolbar 
     headerbar.pack_start (open_button) 
     show_all() 
     this.set_titlebar(headerbar) 

     // Box: 
     box:Gtk.Box = new Gtk.Box (Gtk.Orientation.VERTICAL, 1) 
     this.add (box) 

     // A ScrolledWindow: 
     scrolled:Gtk.ScrolledWindow = new Gtk.ScrolledWindow (null, null) 
     box.pack_start (scrolled, true, true, 0) 

     // The TextView: 
     _view = new Gtk.TextView() 
     _view.set_wrap_mode (Gtk.WrapMode.WORD) 
     _view.buffer.text = "Lorem Ipsum" 
     scrolled.add (_view) 

    def openfile (self:ToolButton) 

     var dialog = new FileChooserDialog ("Open file", 
             this, 
             FileChooserAction.OPEN, 
             Stock.OK,  ResponseType.ACCEPT, 
             Stock.CANCEL, ResponseType.CANCEL) 
     //filter.add_pixbuf_formats() 
     //dialog.add_filter (filter) 

     case dialog.run() 
      when ResponseType.ACCEPT 
       var filename = dialog.get_filename() 
       //image.set_from_file(filename) 

     if (dialog.run() == Gtk.ResponseType.ACCEPT) 
      _uri = dialog.get_uri() 
      stdout.printf ("Selection:\n %s", _uri) 

     dialog.destroy() 

Oder sollte ich nicht Sorgen überhaupt über _variables Akkumulieren?

Antwort

3

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
+0

@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! –

Verwandte Themen