Сначала замечание по терминологии, а затем обобщение.
Доступ к «глобальной переменной» можно получить в любом месте вашей программы, поэтому ее область действия является глобальной. _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
стал объектом, представляющим модель. Сделав это отдельным объектом, можно получить несколько представлений одних и тех же данных. Например, при написании вопроса StackOverflow у вас есть окно редактора, а также предварительный просмотр. Оба являются разными представлениями одних и тех же данных.
В этом примере панель инструментов заголовка была дополнительно разделена на разные объекты с помощью шаблона команды. Каждая кнопка на панели инструментов имеет связанную с ней команду. Имея команды как отдельные объекты, команду можно использовать повторно. Например, привязка клавиш 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 Items указано "Использовать именованный значок "document-open" или метку "_Open"." Так что я использовал
document-open
. Эти имена взяты из Спецификации именования значков freedesktop.org а>
- для кнопки OK в диалоговом окне выбора файла Документация для разработчиков GNOME гласит: "Не используйте значок. Используйте метку "_OK"." Подчеркивание перед означает, что оно интернационализировано и переведено как
gettext
. gettext
использует «домены», которые являются файлами перевода. Для GTK+3 домен называется gtk30
. Чтобы включить gettext
, когда ваша программа скомпилирована, макрос для домена по умолчанию должен быть передан компилятору C. Вот почему -X -DGETTEXT_PACKAGE
необходим. Также в Genie программа Intl.setlocale()
необходима для установки локали для среды выполнения. Когда это будет сделано с использованием чего-то вроде LC_ALL="zh_CN" ./text_editor_example
для запуска вашей программы, отобразится кнопка «ОК» на китайском языке, если у вас установлена эта локаль.
person
AlThomas
schedule
23.04.2016