Является ли класс Download плохим кандидатом на неизменность? - PullRequest
2 голосов
/ 17 ноября 2009

У меня есть класс, который я использую для загрузки. Класс является наблюдаемым, и я использую его как объект модели UI свинга. Однако это слишком медленно, как для запуска (с http-рукопожатия, я полагаю, так мало я могу с этим поделать, вероятно), так и с синхронизированного доступа. Интерфейс класса:

public File getDownloadedFile() 
public String getMimeType() 
public synchronized BigInteger getExpectedSize() 
public URL getURL()
public synchronized boolean cancel()
public synchronized boolean retry()
public synchronized boolean isDone()
public synchronized boolean isCancelled() 
public synchronized BigInteger getProgress() 
public String getName() 
public synchronized BigInteger getBytesRead() 
public synchronized BigDecimal getBytesPerSecond() 
public synchronized BigDecimal getTimeRemaining() 
public synchronized void start()

Я хочу исключить синхронизированные файлы, но я знаю, что для этого мне нужно сделать способ сделать этот класс неизменным. Я попытался смоделировать это как конечный автомат (скрытый внутри), однако среднее (работающее) состояние имеет InputStream + поток. Как правильно превратить это в неизменную обертку? 1) Превратить класс в обертку для конечного автомата + неизменное состояние. Начните запуск первого состояния машины, затем уведомляйте наблюдателей о каждом изменении состояния с помощью неизменной копии. (Однако ожидание клиента монолитного класса - для модели Swing UI для бывших, проблематично.

2) Откажитесь от использования обертки в качестве модели качелей. просто зарегистрируйте мой контроллер в качестве наблюдателя, запустите и добавьте первое (неизменяемое) состояние в качестве модели и используйте неизменный идентификатор состояния (например, URL-адрес загрузки), чтобы распознать состояние конечного автомата загрузки, отправляемое notifyObservers (), как принадлежащее некоторой загрузке , чтобы заменить каждое последующее состояние + обновить пользовательский интерфейс. Однако я не думаю, что это очень удобно, когда это используется без интереса к уведомлению. По крайней мере, было бы неплохо ждать завершения загрузки?

Ответы [ 4 ]

2 голосов
/ 17 ноября 2009

Я думаю, ваша первая ошибка - попытаться превратить этот класс в модель Swing. Глядя на список методов, кажется, что единственная причина, по которой вы это сделали, - позволить некоторому компоненту Swing опрашивать объект, чтобы сообщить о прогрессе. Это нарушает принцип «говори, не спрашивай» ОО дизайна.

Создайте отдельный объект DownloadProgress и попросите ваш объект Download регулярно обновлять его. Поскольку этот объект содержит только поля данных, его методы доступа могут быть синхронизированы. Если вы хотите, чтобы он отправлял уведомления в компонент Swing (опять же, скажите, не спрашивайте), тогда ваши средства доступа помещают в очередь обратный вызов, используя SwingUtilities.invokeLater () .

Это оставляет контроль над фактической загрузкой. Если вы используете Семафор , то вы можете установить его из одного потока (я буду считать поток диспетчеризации событий Swing) и читать его из другого (поток, который фактически выполняет чтение). Возможно, вам придется предпринять более решительные действия, например, закрыть сокет под читателем. Однако это совершенно другая тема, нежели синхронизация.

Ах да, вы выполняете загрузку в фоновом потоке, верно? Не в потоке отправки событий Swing?

0 голосов
/ 19 ноября 2009

Хороший ответ. И вы правы в том, что этот класс используется в качестве запроса для средства визуализации. Прямо сейчас я «решил» сделать его неизменным, держа каждое изменяемое свойство в неизменяемом (конечном автомате) энергозависимом объекте (который обновляется при каждом обновлении из (изменчивого) потока в другом потоке). Таким образом, получение этого объекта из потока (один раз) и рендерера (инстанцированного ServiceLoader - это форма DI) запрашивает его для соответствующих полей - информации - чтобы выбрать, что следует визуализировать в моем компоненте JDownloadList. Даже если ваш другой совет звучит так: «говорите, не спрашивайте», я не собираюсь его выполнять, просто чтобы гибкость различных средств визуализации использовала информацию о загрузке по-разному без большого количества дополнительного установщика на устройстве визуализации (что в любом случае имеет состояние загрузки). Мне просто нужно быть осторожным, чтобы запрашивать объект только один раз при каждом рендере, и я должен возможность удалить синхронизированную.

Правильно? Правильно?

0 голосов
/ 17 ноября 2009

То, как API структурирован, означает, что этот класс должен каким-то образом инкапсулировать изменяемое состояние.

Однако тот факт, что класс является изменчивым, не означает, что вам нужно добавлять ключевое слово synchronized в каждый метод.

Вы должны быть в состоянии ограничить свою потребность в синхронизации с гораздо меньшими блоками внутри кода - или даже полностью устранить ее, используя некоторые из классов Atomic.

Я настоятельно рекомендую прочитать Параллелизм Java на практике , который я считаю окончательной работой по реализации параллелизма в программах Java. Конечно, он не проливает свет на самые темные углы, но он, безусловно, поможет вам понять, что безопасно, что небезопасно и чего следует избегать без значительной подготовки ...

0 голосов
/ 17 ноября 2009

С моей точки зрения, если вы сделаете так, чтобы этот класс мог загружать только один файл, он в основном неизменен. Да, некоторые состояния могут измениться как побочный продукт выполнения своей работы, но все изменения являются внутренними. Строго говоря, он не является неизменным, поскольку имеет изменяемое состояние, но с практической точки зрения он неизменен, поскольку не имеет методов, позволяющих программисту изменять эти состояния.

Мне кажется, я не совсем понимаю вашу общую цель. Это так, что код, сообщающий об оставшемся времени загрузки, получает фиксированный снимок оставшегося времени (например) или метод, отображающий имя файла, не может также отменить загрузку (например)?

...