Потому что мы оперируем абстрактным потоком данных. В данном случае потоком кармы. Зачем кэширующему слою знать логин пользователя, зачем ему знать, по какому ключу и куда нужно складывать кэш? Тем более, что в случае обобщения будет неизвестно какой ключ, а то и вовсе ключа не будет (например, для единственного в своем роде потока данных). Пусть это знание про структуру кэша останется извне:
Никто не заставляет использовать этот оператор непосредственно, можно на его основе построить кэширующий оператор, складывающий в Dictionary и его уже везде использовать. Важно лишь то, что нужно делать маленькие кирпичики и из них собирать стену, а не лить монолитом весь дом. Потому что из кирпичей ещё много чего полезного очень быстро можно собрать.
Disclaimer: чукча не C#-писатель и код не проверялся, так что где-то могут быть ошибки, важна идея.
Для начала разделим получение кармы и кэширование:
public static IObservable<KarmaModel> GetKarma(string userName) {
return Observable.Create<KarmaModel>(observer =>
Scheduler.Schedule(async () => {
var karmaResponse = await HttpClient.Get(userName);
if (!karmaResponse.IsSuccessful) {
observer.OnError(karmaResponse.Exception);
} else {
observer.OnNext(karmaResponse.Data);
observer.OnCompleted();
}
})
);
}
public static IObservable<KarmaModel> Cache(this IObservable<KarmaModel> source, ICache cache, string userName) {
return Observable.CreateWithDisposable<KarmaModel>(observer =>
// Provide value from cache immediately if exists.
KarmaMode karma = null;
if (cache.HasCached(userName)) {
karma = cache.GetCachedItem(userName);
observer.OnNext(karma);
}
// Then just forward values to observer, caching last value.
source.Subscribe(
(karma) => {
if (karma != null && updatedKarma.LastModified > karma.LastModified) {
cache.Put(updatedKarma)
observer.OnNext(karma)
}
},
observer.OnError,
observer.OnCompleted
);
);
}
теперь уберём ненужную зависимость кэширующего оператора от имени пользователя:
public interface ICache {
bool Empty();
KarmaModel Get();
void Put(KarmaModel);
}
public static IObservable<KarmaModel> Cache(this IObservable<KarmaModel> source, ICache cache) {
// Изменения очевидны
}
А что это за проверка на LastModified? Для этого есть DistinctUntilChanged с подходящим компаратором.
Итого получить что-то вроде:
var userName = "foo";
var userCache = globalCache.getByKey(userName);
var karma = new GetKarma(userName).Cache(userCache).DistinctUntilChanged(new KarmaLastModifiedComparator)
И теперь уже чётко видно, что куски можно обобщить и переиспользовать.
Сила реактивных расширений в том, что мы собираем pipeline из элементарных компонентов. В частности, кэширование можно представить в виде преобразования над Observable и в дальнейшем переиспользовать. В данном же случае загрузка данных из сети и кэширование смешаны, смысла в Observable почти ноль.
Расширение, видно, что-то грузит по HTTP, что приводит к тому, что на HTTPS-сайтах пропадает значок защиты, по крайней мере на Safari. Сбивает с толку, когда ты по привычке проверяешь, что ты находишься на правильном сайте почты или банка.
Инкрементальный парсер в этой статье и описан. Он же перепарсивает с точки изменения, это в статье указано. Не думаю, что это как-то связано с упомянутым парсингом «по уровням вложенности».
Возможно, я чего-то не понял. Но как можно парсить не углубляясь внутрь блоков? Ведь возможно что закрывающая фигурная скобка без отступа это на самом деле не конец объявления функции, а всего лишь часть строки или блочного комментария, начатого в самой первой функции, но глубоко внутри.
Я начал подходить к этой же задаче несколько с другой стороны — генерировать ресурсы из SVG при сборке. Это, конечно, ограничивает область применения (во время выполнения программы нельзя изменять размеры/цвета/...), но зато и проблем с отображением меньше, так как для растеризации используется достаточно мощная библиотека Batik.
2-3. Пусть есть наш interface Intf, внешний класс class Cls implements Intf и наш метод void smth(Intf i). Мы хотим новую функциональность, создаем interface Intf2 extends Intf, теперь нам нужно или добавить новый метод void smth(Intf2 i), оставив старый, или изменить метод на void smth(Intf2 i) и создать адаптер
class Intf1ToIntf2Adapter implements Intf2 {
private Intf1 intf1;
public Intf1ToIntf2Adapter(Intf1 intf1) { this.intf1 = intf1; }
public void oldMethod() { intf1.oldMethod(); }
public void newMethod() { /* some default impl */ }
}
Так вот default method — практически автоматизация второго подхода.
1. Пусть предположение о том, что совместимость не ломается, верно. Что вернёт только что добавленный метод интерфейса «int getInt()», будучи вызванным на классе, реализующем этот интерфейс, но скомпилированном со старой его версии?
2. Методы, использующие этот интерфейс, тоже придётся дублировать
3. Библиотеки, использующие разные версии интерфейсов, не смогут взаимодействовать друг с другом, т.е. все равно все перекомпилировать
4. Записали, распишитесь вот здесь ________
Множественное наследование так страшно из-за данных в предках (порядок инициализации полей, множественное непрямое наследование от одного базового класса). Поля в интерфейсы добавлять по-прежнему нельзя, так что про множественное наследование речи не идет.
Кроме того, что это позволит добавлять методы в интерфейс, не ломая бинарной совместимости, это ещё и позволит определять богатые интерфейсы, не вынуждая реализации к определению всех методов.
Например, новому классу коллекции достаточно будет определить метод foreach, и все остальные полезные методы (map, filter, fold, ...) он получит автоматически, имея все же возможность их переопределить, если это повысит эффективность.
Похожим образом на traits построены коллекции в Scala — очень удобно.
Вот javascript-библиотека Tangle для создания интерактивных документов, вдруг пригодится.
И вообще на сайте у Bret Victor есть что почитать на тему интерактивных интерфейсов.
Для использования нужно:
* Поставить Android SDK, прописать ANDROID_HOME
* Поставить Scala 2.8.2, в IDEA создать Global library «scala-2.8.2», в которую добавить scala-compiler.jar и scala-library.jar
* Поставить SBT
Теперь открываем проект. Создана одна конфигурация запуска, отладка работает. Будут вопросы, проблемы — обращайтесь в личку. Обратите внимание на класс TR — фича Sсala/SBT.
SBT (Simple build tool) — ни разу не simple, но весьма мощный инструмент, хотя и требуется много времени, чтобы понять его устройство. Пара трюков описана у меня в молодом блоге.
Никто не заставляет использовать этот оператор непосредственно, можно на его основе построить кэширующий оператор, складывающий в Dictionary и его уже везде использовать. Важно лишь то, что нужно делать маленькие кирпичики и из них собирать стену, а не лить монолитом весь дом. Потому что из кирпичей ещё много чего полезного очень быстро можно собрать.
Для начала разделим получение кармы и кэширование:
теперь уберём ненужную зависимость кэширующего оператора от имени пользователя:
А что это за проверка на LastModified? Для этого есть DistinctUntilChanged с подходящим компаратором.
Итого получить что-то вроде:
И теперь уже чётко видно, что куски можно обобщить и переиспользовать.
Проект доступен на https://github.com/kriomant/buketan
Пока возможностей не слишком много, но если кого заинтересует, то продолжу развитие.
Так что для каждой правки приходится скроллить одновременно две панели: исходного текста и предпросмотра.
Это пока единственное, что останавливает меня от активного использования retext, так как редактируемые документы в основном довольно объемные.
2-3. Пусть есть наш
interface Intf
, внешний классclass Cls implements Intf
и наш методvoid smth(Intf i)
. Мы хотим новую функциональность, создаемinterface Intf2 extends Intf
, теперь нам нужно или добавить новый методvoid smth(Intf2 i)
, оставив старый, или изменить метод наvoid smth(Intf2 i)
и создать адаптерТак вот default method — практически автоматизация второго подхода.
2. Методы, использующие этот интерфейс, тоже придётся дублировать
3. Библиотеки, использующие разные версии интерфейсов, не смогут взаимодействовать друг с другом, т.е. все равно все перекомпилировать
4. Записали, распишитесь вот здесь ________
Например, новому классу коллекции достаточно будет определить метод foreach, и все остальные полезные методы (map, filter, fold, ...) он получит автоматически, имея все же возможность их переопределить, если это повысит эффективность.
Похожим образом на traits построены коллекции в Scala — очень удобно.
Вот javascript-библиотека Tangle для создания интерактивных документов, вдруг пригодится.
И вообще на сайте у Bret Victor есть что почитать на тему интерактивных интерфейсов.
Для использования нужно:
* Поставить Android SDK, прописать ANDROID_HOME
* Поставить Scala 2.8.2, в IDEA создать Global library «scala-2.8.2», в которую добавить scala-compiler.jar и scala-library.jar
* Поставить SBT
Теперь открываем проект. Создана одна конфигурация запуска, отладка работает. Будут вопросы, проблемы — обращайтесь в личку. Обратите внимание на класс TR — фича Sсala/SBT.
SBT (Simple build tool) — ни разу не simple, но весьма мощный инструмент, хотя и требуется много времени, чтобы понять его устройство. Пара трюков описана у меня в молодом блоге.