Не совсем понял — при использовании флаттер, UI нужно писать отдельно для каждой платформы? Или гугл выпустил какой-то набор компонент, которые по разному отображаются в зависимости от платформы?
Хм, если честно у меня более прозаическая догадка.
Игрался с таким кодом
Заголовок спойлера
public class CustomClass
{
public override string ToString()
{
return "CUSTOM";
}
public virtual object SomeVirtualMethod()
{
return "SomeVirtualMethod";
}
public object SomeMethod()
{
return "SomeMethod";
}
}
[StructLayout(LayoutKind.Explicit)]
public class CustomStructWithLayout
{
[FieldOffset(0)]
public string Str;
[FieldOffset(0)]
public CustomClass SomeInstance;
}
class Program
{
static void Main(string[] args)
{
CustomStructWithLayout instance = new CustomStructWithLayout();
instance.SomeInstance = new CustomClass();
instance.Str = "4564";
Console.WriteLine(instance.SomeInstance.GetType()); //System.String
Console.WriteLine(instance.SomeInstance.ToString()); //4564
Console.WriteLine(instance.SomeInstance.SomeMethod()); // SomeMethod
Console.WriteLine(instance.SomeInstance.SomeVirtualMethod()); // null
}
}
И как мне кажется, для виртуального метода CLR генерит метод-заглушку, возвращающий дефолтное значение.
Поскольку, насколько я понимаю, для вызова виртуального метода CLR необходимо действительно найти этот метод в иерархии типа, и так как метода SomeVirtualMethod нет в иерархии типа string, то и возвращается эта заглушка.
Разрядка для мозгов: Что будет выведено при вызове виртуального свойства CustomClass?
А можно для не просвящённых почему так?
Я понимаю, какой был бы результат, если бы свойство было не виртуальным, но почему возвращается null для виртуального я не догоняю.
Мне бы хотелось получить что-нибудь вроде MethodNotFoundException.
Не использовать Ajax.BeginForm, использовать FormData, сделать php обработчик и еще тучи советов по увеличению программного кода.
Думаю, стоило прислушаться к совету и не использовать Ajax.BeginForm.
Кода получилось бы меньше, да и получился бы он более поддерживаемым. А с учётом кучи JS библиотек, задачу вы решили бы быстрее.
Да и логику вашего обработчика/хелпера я так и не понял.
Очень удобная штука. Вынесли валидацию и транзакции
DataAnnotation валидация
public class DataAnnotationsValidationPreProcessor<TRequest> : IRequestPreProcessor<TRequest>
{
public Task Process([NotNull] TRequest request, CancellationToken cancellationToken)
{
var context = new ValidationContext(request);
var results = new List<ValidationResult>();
if (Validator.TryValidateObject(request, context, results))
{
return Task.CompletedTask;
}
var errors = results.Select(x => new ValidationFailure(x.MemberNames.First(), x.ErrorMessage));
throw new ValidationException(errors);
}
}
"Не пользуйтесь ООП в ООП"? Ну круто, теперь заживём.
ООП это не только наследование. Я не призываю отказываться от наследования совсем, но мой подход чем реже, тем лучше.
А если писать код как попало, то это "частенько" ведёт к нарушению всего SOLID. Это не причина.
Серебряной пули и правда нет. Но есть best practices и они появились не с пустого места. А насчёт аббревиатур — это удобный способ донести мысль другому человеку по-быстрому.
Ага. Все рекламируют композицию и делегирование, а что в большинстве языков нет реализации интерфейса через член, советующих не волнует — ну, крутись как хочешь, плоди сотни строк непродуктивного кода.
Везде свои компромиссы. Хотите хорошую систему, с которой приятно работать и удобно вносить изменения — делегируйте; проект небольшой — колбасьте код как угодно.
Да и про какие сотни строк вы говорите? Если у вас есть класс, который реализует так много интерфейсов, то проблема возникла раньше. Опять же из-за не следования хорошим практикам.
Жить вообще опасно. Просто если хочешь унаследоваться вместо композиции — подумай 10 раз, унаследоваться глубоко — 100, унаследоваться множественно — 1000.
Об этом и речь — зачем усложнять и думать 10-100-1000 раз, если можно сделать просто?
Интерфейсы ни от чего не спасают. Это ужасная сущность с точки зрения развития системы, потому что их изменять вообще невозможно. Любое изменение — всё, система сломана.
Интерфейсы это ваш контракт. Это ваш api, если хотите. И если контракт меняется, значит на то была причина — изменилось требуемое поведение.
Чем это лучше классов, где что-то в предках изменилось, и вдруг поломался потомок? Ну, хотя бы есть ненулевой шанс, что оно будет работать. Изменение интерфейса ломает систему всегда.
В том и проблема, что оно может будет работать. А может не будет. А может будет работать не так, как надо. А может появиться новое поведение, которое не ожидалось. В любом случае, чтобы быть уверенным придётся проверить всех наследников. С композицией надо проверить только там, где изменилось.
Рассуждение выше про интерфейсы немного устаревает с введением костыля под названием "default interface implementation" — интерфейсы теперь становятся недо-классами.
Согласен, default interface implementation неоднозначная фича. Пока что, я вижу ей применение для добавление утилитарного поведения, вроде того же observer'a.
Кстати, насчёт глубокой цепочки наследования в GUI — тот же реакт построен на High Order Components и там этот подход весьма органичен.
Лениться делегировать — это экономия на спичках. Больше огребётесь от огромного количества наследников.
Наследование это не способ писать меньше кода. Это способ выразить отношение is-a (ну и ещё способ сделать discriminated union, в языках где его нет). Более того, я считаю что наследование как раз более вредно — чем чаще вы наследуете, тем более костной становится ваша система.
Примеры:
У вас есть базовый класс, который часто наследуется в вашей системе. Затем, только некоторому числу наследников понадобилось новое поведение — вы меняете базовый класс, но вместе с этим вы также меняете и контракт тех классов, которым это поведение не нужно. Как итог, вам нужно протестировать те компоненты, которые даже не менялись — нарушение OCP. А ещё частенько ведёт и к нарушению LSP.
В укор первому примеру, вы можете сказать — "да я щас наделаю много мелких классов (например, VisibleAndSolid, VisibleAndMovable, VisibleAndSolidAndMovable) с точечным поведением и буду множественно наследовать от них". Ок, но чего вы этим сэкономите? Количество LOC будет примерно сравнимым при композиции. Только в этот раз вы усложнили систему, наделав в ней кучу ненужных сущностей.
Имея некоторый базовый класс, вы делаете вид, что знаете как он будет использоваться. Нарушение инкапсуляции здесь ещё грубее — каждый разработчик должен знать детали реализации в базовом классе (иначе опять можно нарушить LSP).
Ещё более опасна длинная цепочка наследования. Например, есть у вас некоторая иерархия с некоторым поведением в самом верхнем родителе. Затем, одному или нескольким наследникам нужно отличное поведение. Как итог порождается ещё одна иерархия классов, что в конечном итоге ведёт к сложности системе.
Ещё вопрос — неужели всех устраивает монструозный (как по мне) трёхэтажный синтаксис описания виджетов? Есть ли планы по разработке подобия jsx?
Понял, спасибо.
Для react native существует, например, nativebase.io, который под каждую платформу отображает нативный элемент.
Видимо, для флаттер придётся подождать сообщества.
Не совсем понял — при использовании флаттер, UI нужно писать отдельно для каждой платформы? Или гугл выпустил какой-то набор компонент, которые по разному отображаются в зависимости от платформы?
Хабр съел ссылку. Подход называется Contextual Validation. Гуглится на сайте Фаулера.
https://martinfowler.com/bliki/ContextualValidation.html
Про Do/CanDo в своё писал Фаулер.
Да я понял о каких книгах речь, спасибо :)
Ну так какую?
Хм, если честно у меня более прозаическая догадка.
Игрался с таким кодом
И как мне кажется, для виртуального метода CLR генерит метод-заглушку, возвращающий дефолтное значение.
Поскольку, насколько я понимаю, для вызова виртуального метода CLR необходимо действительно найти этот метод в иерархии типа, и так как метода
SomeVirtualMethod
нет в иерархии типаstring
, то и возвращается эта заглушка.А можно для не просвящённых почему так?
Я понимаю, какой был бы результат, если бы свойство было не виртуальным, но почему возвращается
null
для виртуального я не догоняю.Мне бы хотелось получить что-нибудь вроде
MethodNotFoundException
.Думаю, стоило прислушаться к совету и не использовать Ajax.BeginForm.
Кода получилось бы меньше, да и получился бы он более поддерживаемым. А с учётом кучи JS библиотек, задачу вы решили бы быстрее.
Да и логику вашего обработчика/хелпера я так и не понял.
Не так давно я мониторил место на диске и обнаружил 4гб папку в профиле хрома — indexeddb от my.mail.ru. При этом я даж хз когда я туда заходил.
юзайте сагу наконец
Ну, в общем-то, в статье. Под заголовками "Код без cross-cutting concern" и "Код с cross-cutting concern"
Всё просто, на самом деле. Смотрите здесь https://github.com/jbogard/MediatR/wiki/Behaviors
Очень удобная штука. Вынесли валидацию и транзакции
rider нынче позволяте дебажить чужие исходники
Не то, чтобы это прям решение для всех, но в решарпере есть возможность делегировать реализацию в один клик.
ООП это не только наследование. Я не призываю отказываться от наследования совсем, но мой подход чем реже, тем лучше.
Серебряной пули и правда нет. Но есть best practices и они появились не с пустого места. А насчёт аббревиатур — это удобный способ донести мысль другому человеку по-быстрому.
Везде свои компромиссы. Хотите хорошую систему, с которой приятно работать и удобно вносить изменения — делегируйте; проект небольшой — колбасьте код как угодно.
Да и про какие сотни строк вы говорите? Если у вас есть класс, который реализует так много интерфейсов, то проблема возникла раньше. Опять же из-за не следования хорошим практикам.
Об этом и речь — зачем усложнять и думать 10-100-1000 раз, если можно сделать просто?
Интерфейсы это ваш контракт. Это ваш api, если хотите. И если контракт меняется, значит на то была причина — изменилось требуемое поведение.
В том и проблема, что оно может будет работать. А может не будет. А может будет работать не так, как надо. А может появиться новое поведение, которое не ожидалось. В любом случае, чтобы быть уверенным придётся проверить всех наследников. С композицией надо проверить только там, где изменилось.
Согласен, default interface implementation неоднозначная фича. Пока что, я вижу ей применение для добавление утилитарного поведения, вроде того же observer'a.
Кстати, насчёт глубокой цепочки наследования в GUI — тот же реакт построен на High Order Components и там этот подход весьма органичен.
Лениться делегировать — это экономия на спичках. Больше огребётесь от огромного количества наследников.
Наследование это не способ писать меньше кода. Это способ выразить отношение is-a (ну и ещё способ сделать discriminated union, в языках где его нет). Более того, я считаю что наследование как раз более вредно — чем чаще вы наследуете, тем более костной становится ваша система.
Примеры:
У вас есть базовый класс, который часто наследуется в вашей системе. Затем, только некоторому числу наследников понадобилось новое поведение — вы меняете базовый класс, но вместе с этим вы также меняете и контракт тех классов, которым это поведение не нужно. Как итог, вам нужно протестировать те компоненты, которые даже не менялись — нарушение OCP. А ещё частенько ведёт и к нарушению LSP.
В укор первому примеру, вы можете сказать — "да я щас наделаю много мелких классов (например, VisibleAndSolid, VisibleAndMovable, VisibleAndSolidAndMovable) с точечным поведением и буду множественно наследовать от них". Ок, но чего вы этим сэкономите? Количество LOC будет примерно сравнимым при композиции. Только в этот раз вы усложнили систему, наделав в ней кучу ненужных сущностей.
Имея некоторый базовый класс, вы делаете вид, что знаете как он будет использоваться. Нарушение инкапсуляции здесь ещё грубее — каждый разработчик должен знать детали реализации в базовом классе (иначе опять можно нарушить LSP).
Да и вообще сто раз это исписанно.
ps. В java (и скоро в c#) ведь есть partial interface implementation — пользуйтесь.