2016-04-10 12 views
0

以下は、GenieのToolbarButtonの作業コードです。目的は、選択されたファイルのURIを取得し、クラスのconstruct/initに戻すことです。問題は、すべての例で、私はグローバル_変数が使用されていることです(下のコードを参照)。直感的ではないように見えますが、コードが大きくなるとバグを取り除くのがより難しくなります。これらの変数は蓄積され始めるからです。関数openfileを作成する他の方法は、クラスの構造体/ init内の通常の変数にuriを返しますか?ここで Genieのグローバル変数の回避

はコードです:

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() 

または私は_variablesが蓄積については全く心配しないでしょうか?

答えて

3

最初に用語に関する注釈とその後の一般化。

「グローバル変数」は、プログラム内のどこにでもアクセスできるため、スコープはグローバルです。あなたの質問で参照している_variablesは、オブジェクトのスコープ内のプライベートフィールドです。それらは、そのオブジェクトで定義されているコードによってのみアクセスできます。しかし、あなたはあなたのオブジェクト内の私的な作業変数の蓄積について心配するのは正しいです。

オブジェクトを設計することは難しく、技術とアイデアは数十年の実践と研究で進化しました。 Michael Feathersによって紹介されたSOLID頭字語は、デザインを評価するための有用な基準を提供するオブジェクト指向設計の5つの原則をまとめたものです。ガンマ(Gamma)他による書籍、Design Patterns: Elements of Reusable Object-Oriented Softwareもまた、そして1994年に最初に出版され、オブジェクト指向プログラミングにおけるデザインの良い要約と分類を提供します。この本は、そのようなパターンの使用を実証するためのケーススタディとして文書エディタを使用しています。本書のSOLIDの原則とデザインパターンはどちらも抽象であり、プログラムの作成方法を教えてくれませんが、プログラマーが議論して評価できる共通のアイデアを提供しています。だから私は私の答えでこれらのツールを両方使うつもりですが、近年、ソフトウェア開発プロセスをさらに強化するために、具体的にはtest driven developmentbehaviour driven developmentという追加の技術が開発されていることをご承知おきください。

SOLIDのSはSingle Responsibility Principleの略で、あなたの例を見るのに適しています。あなたのオブジェクトApplicationを呼び出し、プライベートな作業変数をグローバル変数として考えると、あなたは単一のオブジェクト内にアプリケーション全体を記述していることを示唆しています。あなたができることは、Applicationを、単一の責任領域にもっと重点を置く多くの異なるオブジェクトに分離して開始することです。最初に私はApplicationオブジェクトの名前を変更すると考えました。私はEditorWindowに行きました。下の私の例ではEditorWindowHeaderDocumentViewを持っています。

で以下のコードをコンパイル:

valac -X -DGETTEXT_PACKAGE --pkg gtk+-3.0 text_editor_example.gs

-X -DGETTEXT_PACKAGEの使用は、この回答の最後に説明されています。

[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() 

グラフィックユーザーインターフェイスの一般的な高レベルパターンはmodel-view-controller (MVC)です。これは、オブジェクトを簡単に再利用して変更できるようにオブジェクトのデカップリングを行うことです。例ではdocumentがモデルを表すオブジェクトになっています。これを別個のオブジェクトにすることにより、同じデータを複数のビューで表示することができます。たとえば、StackOverflowの質問を書くときには、エディタウィンドウがありますが、プレビューもあります。両方とも同じデータの異なるビューです。

この例では、command patternを使用して、ヘッダーツールバーが別のオブジェクトにさらに分離されています。ツールバーの各ボタンには、関連するコマンドがあります。コマンドを別々のオブジェクトとして持つことにより、コマンドを再利用することができます。例えば、Ctrl-Oをバインドする鍵は、Loadコマンドを使用することもできます。このように、開いているドキュメントボタンにアタッチされたコマンドのコードは、Ctrl-Oにアタッチするために書き直す必要はありません。

コマンドパターンはインターフェイスを使用します。オブジェクトが​​メソッドを実装している限り、コマンドとして使用できます。 Loadコマンドは、ユーザーにどのURIを開くかを尋ねるオブジェクト用のインターフェイスも使用します。 Gtk +にはFileChooserNativeもあります。したがって、FileChooserDialogの代わりにFileChooserNativeダイアログを使用する場合は、DocumentSelectorインターフェイスを実装する新しいオブジェクトを作成し、代わりにLoadコマンドに渡すだけで済みます。このようにオブジェクトをデカプリングすることで、プログラムの柔軟性が増し、プライベートフィールドの使用が各オブジェクトに限定されたままになります。

例として、コンパイル時にいくつかの警告:warning: Gtk.Stock has been deprecated since 3.10がありました。ドキュメント・オープン 『またはラベル「_Open開いているドキュメントのアイコンthe GNOME developer documentation for Stock Items状態『アイコンという名前の使用』のために

  • :この答えの例では、新しい方法を使用しています』。」だから私はdocument-openを使った。これらの名前は、ファイル選択ダイアログの[OK]ボタンのfreedesktop.org Icon Naming Specification
  • からのものです。GNOME Developer documentationには「アイコンを使用しないでください。前のアンダースコアは、それが国際化され、gettextによって翻訳されたことを意味します。 gettextは、翻訳ファイルである「ドメイン」を使用します。 GTK + 3の場合、ドメインはgtk30となります。プログラムをコンパイルするときにgettextを有効にするには、デフォルトドメインのマクロをCコンパイラに渡す必要があります。これが-X -DGETTEXT_PACKAGEが必要な理由です。また、Genieプログラムでは、ロケールをランタイム環境に設定するためにIntl.setlocale()が必要です。これはあなたのプログラムを実行するためにLC_ALL="zh_CN" ./text_editor_exampleのようなものを使用して行われた場合、あなたがロケールが、これは私がGitHubので読んでいる洙はるかに明確なコードになります
+0

@AIThomasをインストールしていることがあれば、中国でOKボタンが表示されます!あなたはそのような偉大な合成をしました!ありがとうございました! –