Pull to refresh
27
0
kriomant @kriomant

User

Send message
Тем, кому не хочется возиться с программированием, я бы порекомендовал Autodesk Fusion 360, для хобби-проектов он бесплатен.
Потому что мы оперируем абстрактным потоком данных. В данном случае потоком кармы. Зачем кэширующему слою знать логин пользователя, зачем ему знать, по какому ключу и куда нужно складывать кэш? Тем более, что в случае обобщения будет неизвестно какой ключ, а то и вовсе ключа не будет (например, для единственного в своем роде потока данных). Пусть это знание про структуру кэша останется извне:

bla-bla.Cache(new ICache { Karma Get() { return cache.Get(userName); }; void Put(Karma karma) { cache.Put(userName, karma); ... })


Никто не заставляет использовать этот оператор непосредственно, можно на его основе построить кэширующий оператор, складывающий в 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 почти ноль.
А можете сделать групповые операции над заметками? Надоедает по одной в другой блокнот перемещать, например.
Я тоже подобное делал, только встраивался в процесс билда: github.com/kriomant/buketan
Да, я тоже пока выключил. Безопасность важнее.
Расширение, видно, что-то грузит по HTTP, что приводит к тому, что на HTTPS-сайтах пропадает значок защиты, по крайней мере на Safari. Сбивает с толку, когда ты по привычке проверяешь, что ты находишься на правильном сайте почты или банка.
Инкрементальный парсер в этой статье и описан. Он же перепарсивает с точки изменения, это в статье указано. Не думаю, что это как-то связано с упомянутым парсингом «по уровням вложенности».
Возможно, я чего-то не понял. Но как можно парсить не углубляясь внутрь блоков? Ведь возможно что закрывающая фигурная скобка без отступа это на самом деле не конец объявления функции, а всего лишь часть строки или блочного комментария, начатого в самой первой функции, но глубоко внутри.
Кроме сообщества Google+ можно использовать группу в Google Groups.
Я начал подходить к этой же задаче несколько с другой стороны — генерировать ресурсы из SVG при сборке. Это, конечно, ограничивает область применения (во время выполнения программы нельзя изменять размеры/цвета/...), но зато и проблем с отображением меньше, так как для растеризации используется достаточно мощная библиотека Batik.

Проект доступен на https://github.com/kriomant/buketan
Пока возможностей не слишком много, но если кого заинтересует, то продолжу развитие.
После фикса бага со сбрасыванием позиции предпросмотра в начало при каждом редактировании стало возможно редактировать документы длиннее одного экрана, но по-прежнему неудобно, потому что теперь позиция предпросмотра сохраняется, но не меняется автоматически для просмотра последнего изменений.

Так что для каждой правки приходится скроллить одновременно две панели: исходного текста и предпросмотра.

Это пока единственное, что останавливает меня от активного использования retext, так как редактируемые документы в основном довольно объемные.
1. Как раз нет — вызовется дефолтная реализация.

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 есть что почитать на тему интерактивных интерфейсов.
Скелет sbt-android-scala проекта.

Для использования нужно:
* Поставить 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, но весьма мощный инструмент, хотя и требуется много времени, чтобы понять его устройство. Пара трюков описана у меня в молодом блоге.
На выходных займусь.

Information

Rating
Does not participate
Location
Новосибирск, Новосибирская обл., Россия
Date of birth
Registered
Activity