Как избежать глобальных переменных в Genie

Ниже приведен рабочий код панели инструментов в Genie. Цель состоит в том, чтобы получить URI для выбранного файла и вернуть его обратно в конструкцию /init класса. Проблема в том, что во всех примерах, с которыми я сталкивался, используются глобальные переменные _ (как показано в коде ниже). Это выглядит не интуитивно, и я боюсь, что всякий раз, когда код становится больше, удалять ошибки становится все труднее, так как эти переменные начнут накапливаться. Есть ли другой способ заставить функцию openfile возвращать uri к обычной переменной внутри конструкции /init класса?

Вот код:

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

Или я не должен беспокоиться о накоплении переменных?

1 ответ

Решение

Сначала примечание по терминологии, а затем обобщение.

"Глобальная переменная" может быть доступна в любом месте вашей программы, поэтому ее область действия является глобальной. _variables Вы имеете в виду в своем вопросе частные поля в рамках вашего объекта. Доступ к ним можно получить только с помощью кода, определенного в этом объекте. Однако вы вправе беспокоиться о накоплении частных рабочих переменных в ваших объектах.

Проектирование объектов трудно сделать, и методы и идеи развивались в течение нескольких десятилетий практики и исследований. Аббревиатура SOLID, представленная Майклом Фезерсом, подводит итог пяти принципам объектно-ориентированного проектирования, которые обеспечивают полезные критерии для оценки вашего дизайна. Также книга " Шаблоны проектирования: элементы многоразового объектно-ориентированного программного обеспечения", авторы Gamma et al. и впервые опубликованный в 1994 году, обеспечивает хорошее резюме и категоризацию проектов в объектно-ориентированном программировании. Эта книга использует редактор документов в качестве примера для демонстрации использования таких шаблонов. Принципы SOLID и шаблоны проектирования в книге - это абстракции, они не расскажут вам, как написать программу, но они дают набор общих идей, которые позволяют программистам обсуждать и оценивать. Поэтому я буду использовать оба этих инструмента в своем ответе, но помните, что в последние годы были разработаны дополнительные методы для дальнейшего совершенствования процесса разработки программного обеспечения, в частности разработки, основанной на тестировании, и разработки, основанной на поведении.

S в SOLID означает принцип единой ответственности и является хорошей отправной точкой для рассмотрения вашего примера. Вызывая свой объект, Applicationи если рассматривать частные рабочие переменные как глобальные переменные, то это предполагает, что вы пишете все приложение в одном объекте. Что вы можете сделать, это начать отделяться Application в ряде различных объектов, которые в большей степени сосредоточены на одной области ответственности. Сначала я думал, что переименую Application объект. Я пошел за EditorWindow, В моем примере ниже EditorWindow также имеет Header и DocumentView,

Скомпилируйте код ниже с помощью:

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

Распространенным шаблоном высокого уровня для графических пользовательских интерфейсов является модель-представление-контроллер (MVC). Речь идет о разъединении ваших объектов, чтобы их можно было легко использовать повторно и менять. В примере document стал объектом, который представляет модель. Делая это отдельным объектом, он позволяет нескольким представлениям получить одни и те же данные. Например, при написании вопроса Stackru у вас есть окно редактора, а также предварительный просмотр. Оба являются разными взглядами на одни и те же данные.

В этом примере панель инструментов заголовка была дополнительно разделена на различные объекты с помощью шаблона команды. Каждая кнопка на панели инструментов имеет соответствующую команду. Имея команды как отдельные объекты, команда может быть использована повторно. Например, связывание клавиш Ctrl-O также может использовать Load команда. Таким образом, код для команды, прикрепленной к кнопке открытия документа, не нужно переписывать, чтобы присоединить ее к Ctrl-O.

Шаблон команды использует интерфейс. Пока объект реализует execute() метод, то он может быть использован в качестве команды. Load Команда также использует интерфейс для объекта, который спрашивает пользователя, какой URI открыть. Gtk+ также предоставляет FileChooserNative. Так что, если вы хотите перейти на использование FileChooserNative диалог вместо FileChooserDialog вам просто нужно написать новый объект, который реализует DocumentSelector интерфейс и передать это Load команда вместо Разъединяя объекты таким образом, это делает вашу программу намного более гибкой, а использование закрытых полей ограничивается каждым объектом.

Как примечание, при компиляции вашего примера было несколько предупреждений: warning: Gtk.Stock has been deprecated since 3.10, Пример в этом ответе использует более новый способ:

  • для значка открытого документа в документации разработчика GNOME для Stock Stock указано "Использовать именованный значок"document-open"или метку"_Open"." Итак, я использовал document-open, Эти имена взяты из спецификации именования иконок freedesktop.org.
  • для кнопки "ОК" в диалоговом окне выбора файлов в документации разработчика GNOME говорится "Не используйте значок. Используйте метку"_OK"". Подчеркивание перед означает, что оно интернационализировано и переведено gettext, gettext использует "домены", которые являются файлами перевода. Для GTK+3 домен называется gtk30, Включить gettext когда ваша программа скомпилирована, макрос для домена по умолчанию должен быть передан компилятору C. Вот почему -X -DGETTEXT_PACKAGE нужно. Также в программе Genie Intl.setlocale() необходим для установки локали в среду выполнения. Когда это сделано, используя что-то вроде LC_ALL="zh_CN" ./text_editor_example для запуска вашей программы будет отображаться кнопка ОК на китайском языке, если у вас установлена ​​эта локаль
Другие вопросы по тегам