В чем разница между декоратором, атрибутом, аспектом и чертой?

С точки зрения чистой информатики (или, возможно, вычислительной лингвистики) я хотел бы знать разницу между словами:

  • Декоратор
  • Атрибут
  • Аспект
  • Черта

Различные языки используют эти слова и функции по-разному. В Python, например, декораторы [согласно Python Wiki] (выделено мной):

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

Мне кажется, что это удивительно похоже на инструмент аспектно-ориентированного программирования, такой как PostSharp или DynamicProxy. то есть:

   [Profile]
   private static void SleepSync()
   {
       Thread.Sleep(200);
   }

Источник: Примеры PostSharp

В C# и Java (а также во множестве других языков) атрибуты могут означать шаблон в стиле Decorator (C#) или поле (Java).

А в C++ через boost или PhP через встроенное слово trait мы можем использовать типажи для расширения классов, как показано здесь: https://en.wikipedia.org/wiki/Trait_(computer_programming)

Итак, с «чистой» точки зрения, каковы канонические определения того, чем все это на самом деле является? Есть ли лучший способ определить их?


person Darkenor    schedule 20.10.2016    source источник
comment
Стандарта между языками нет. Кроме того, я не думаю, что все языки поддерживают строгое определение каждого из них. Возьми Яву. Декоратор = Аннотация, Атрибут = Поле, Черта = Метод по умолчанию (возможно, интерфейс)... Аспект, с другой стороны, является скорее концепцией архитектуры программного обеспечения, чем функцией какого-либо одного языка.   -  person OneCricketeer    schedule 27.10.2016
comment
Тогда само собой разумеется, что для каждой из этих связанных вещей существует термин на уровне парадигмы. Мне было бы трудно поверить с теоретической точки зрения, что вы не можете более точно определить их количество.   -  person Darkenor    schedule 27.10.2016
comment
Как часто вам действительно нужно переключаться между языками программирования, чтобы обсудить концепции, а не фактическую терминологию языков? Страница Википедии, посвященная Черте и Аспекту, кажется адекватной моему пониманию с высокого уровня. (Ссылка: en.m.wikipedia.org/wiki/Aspect_(computer_programming) ) Но, если вы говорите о Python, а я говорю о C#, и мы оба говорим о декораторах, то наше понимание, вероятно, отличается.   -  person OneCricketeer    schedule 27.10.2016
comment
Я не уверен, насколько это актуально. Вопрос по теории и информатике. Но ответ часто. Я имею дело с большим количеством разных по разным технологиям.   -  person Darkenor    schedule 27.10.2016
comment
Я вижу большую ценность в этом вопросе. Как для компьютерных, так и для естественных языков семантическое недопонимание может быть легче преодолено, если люди знают, как развивались эти языки. Это облегчает поиск общих понятий и выявление различий в мышлении.   -  person Michael Scheper    schedule 27.04.2017
comment
Я также надеялся, что кто-нибудь сможет прояснить разницу между декоратором и аннотацией. Мой опыт работы как с Java, так и с Python, и, как бы ни были сильны утверждения некоторых разработчиков о правильном термине, я не слышал никаких реальных рассуждений или какой-либо корреляции с каким-либо языком.   -  person Michael Scheper    schedule 27.04.2017
comment
@MichaelScheper Аннотация не изменяет поведение напрямую и не добавляет новое поведение, в отличие от декоратора. Аннотации могут косвенно изменять поведение, но аннотации — это просто метаданные, дополнительная информация о классе.   -  person Tim    schedule 25.07.2017


Ответы (1)


Декоратор

Я думаю о Decorator с точки зрения шаблона проектирования. Шаблоны проектирования распознаются во многих языках, особенно в объектно-ориентированных. Таким образом, декоратор как шаблон является оболочкой, которая добавляет функциональность, отсутствующую в декорируемой функции или классе.

Самый простой пример, который я могу придумать, это функция декоратора. Функция

int foo(int x)

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

int bar(int x, int y) {
    return y*y + foo(x);
}

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

Другим распространенным примером являются классы ввода-вывода в Java. есть основное

OutputStream s

а затем вы можете украсить это, используя более специализированные классы в зависимости от типа обрабатываемых данных или формата, в котором вы хотите прочитать данные:

OutputStream s1 = new FileOutputStream("somefile.txt");

or

OutputStream s2 = new ByteOutputStream("rawdata.hex");

Атрибут

Я бы склонялся к идее С# о том, что атрибут является правильным пониманием, потому что он отличается от декоратора. Атрибуту присваивается семантическое значение, которое может варьироваться от одного использования к другому и даже между API, использующими один и тот же атрибут. Например, у меня может быть две функции:

[Logging] private void a() { ... }
[Security] private void b() { ... }

Я могу назначить один атрибут регистрации и один атрибут безопасности, и то, что означают эти атрибуты, может отличаться от клиентских API, которые проверяют эти атрибуты. Можно использовать log4j для реализации ведения журнала, а можно использовать другой API. Определение здесь гораздо более гибкое и открытое для интерпретации разными сторонами или пользователями моего кода. Конечно, можно использовать атрибут в качестве декоратора, но атрибуты можно использовать и для гораздо большего.

Просто для устранения неоднозначности слово атрибут также используется для обозначения переменных-членов класса. Здесь мы говорим о более широкой и абстрактной концепции присвоения предопределенного семантического значения существующему объекту или классу. Java называет эти аннотации.

Я считаю, что одним из определяющих факторов, определяющих, что это Атрибут, в том смысле, о котором мы говорим, является то, что он не изменяет поведение напрямую, а только косвенно. Например, присвоение чему-либо атрибута [Ведение журнала] никак не меняет его код. Это как прикрепить бирку с именем, которую ищут другие. Когда другая программа или приложение видит тег имени, она делает определенные выводы и может соответствующим образом изменить свое поведение. Но (по крайней мере, в Java) аннотации или атрибуты напрямую ничего не изменяют — опять же, только тег имени. Это может немного отличаться в C# или других языках, поддерживающих атрибуты, и в этом случае я бы считал их более продвинутым атрибутом или чем-то совершенно другим.

Аспект

Аспект в смысле аспектно-ориентированного программирования (АОП) — это своего рода самомодифицирующаяся или самообновляющаяся конструкция кода. Он определяет участок кода как более гибкий (точечный разрез) и позволяет переключать этот конкретный участок между одним или несколькими возможными обновлениями, исправлениями или разными версиями одного и того же участка кода.

Не могли бы вы сделать некоторые из тех же вещей, что и декораторы и атрибуты, используя аспекты? Конечно. Но зачем вы себе эту головную боль? АОП — это следующий шаг по сравнению с ООП, только его следует использовать при необходимости. Когда это необходимо? Когда конкретное приложение имеет много «сквозных проблем», таких как безопасность или ведение журнала, например, банковское приложение. Эти проблемы пересекаются; они выходят за традиционные границы, которые создают хорошо определенные классы и пакеты. Когда вы ведете журнал, это не приносит большой пользы, если вы не регистрируете все; следовательно, эта озабоченность носит сквозной характер. Поэтому, когда вы собираетесь обновить механизм ведения журнала одного класса, будет сложно одновременно изменить все остальные классы и API, но это также необходимо. В противном случае ваше ведение журнала теперь будет непоследовательным и запутанным, и его будет сложнее использовать для устранения неполадок или мониторинга.

Чтобы сделать такие обновления менее головной болью, были введены аспектно-ориентированные языки, такие как AspectJ. Я не сталкивался с «аспектом», который означал бы что-то иное, кроме этого, но может быть что-то, о чем говорилось ранее о декораторах. Конкретный язык может называть что-то аспектом, но это может больше походить на одну из других вещей, которые мы уже обсуждали.

Черта

Черта является синонимом интерфейса, или, по крайней мере, так она выглядит в языках, которые я изучал.

Интерфейс — это концепция ООП, которая объявляет поведение, не реализуя его. Создание этих ожидаемых поведений позволяет им стать общими и вызываться в общем, не заботясь о специфике. Их реализация остается за подклассами.

Классический пример ООП — это Animal с двумя подклассами, Cat и Dog.

public interface/trait Animal {
    public void speak();
}
public class Cat implements Animal {
    public void speak() {
        output("Meow.");
    }
}
public class Dog implements Animal {
    public void speak() {
         output("Bark!");
    }
}

Это также отличный пример полиморфизма — одно из тех слов, которые обычно вызывают у людей, не связанных с компьютерами, съеживание. Это просто означает, что кошка и собака имеют индивидуальное поведение, и если я объявлю объект Animal, мне все равно, какой тип вы мне дадите. Вы можете подарить мне кошку или собаку. Поскольку оба являются животными, в любом случае все, что мне нужно сделать, это вызвать функцию speak() моего объекта Animal, и я могу быть уверен, что в любом случае будет получен правильный результат. Каждый отдельный подкласс знает, что ему нужно делать. В C++ вода здесь немного более мутная, но общая концепция такая же (прочитайте ключевое слово «виртуальный», если вы хотите начать спускаться по этой кроличьей норе).

Подведение итогов

Я надеюсь, что это проясняет некоторую путаницу. Кажется, что, как вы сказали, многие разные языки имеют много разных значений для каждого из них, что, несомненно, способствует путанице. Я полагаю, что если вы вникнете в это глубже, то обнаружите, что по большому счету это стандартные значения. Я программировал на многих языках (VB, C++, C#, Java, Paschal, PHP, Perl) более 18 лет, и мне удобнее всего считать эти определения своего рода стандартами.

Я, безусловно, приветствую дальнейшее обсуждение того, что я сказал.

person Tim    schedule 02.11.2016
comment
ИМХО черта выходит далеко за рамки интерфейса. Интерфейс только объявляет, какие методы можно найти, но не предоставляет реализацию. Черты также включают в себя определение. Поэтому я рассматриваю черту скорее как своего рода частичный класс, который может быть частичным для нескольких классов. - person derM; 22.06.2017
comment
@derM Как указано в моем ответе, разные языки имеют разные значения. Я согласен с тем, что трейты МОГУТ выходить далеко за рамки простого интерфейса. Я никогда не говорил, что они не могут быть чем-то большим, чем это. Когда вы разбираете, что означают черты всегда и что они могут означать иногда, вы приходите к выводу, что интерфейс является общим знаменателем. Я согласен, что здесь есть двусмысленность, но такова природа обсуждения. Не стесняйтесь публиковать свой собственный ответ, который лучше объясняет черты. - person Tim; 22.06.2017
comment
Обратите внимание, что атрибуты .NET связаны не с объектами (экземплярами), а с сущностями системы типов (например, типами, полями, свойствами, методами и параметрами). Я склонен рассматривать атрибуты как беспомощные теги или метки, которые могут предоставить большие возможности компилятору, среде выполнения или пользовательскому коду с использованием отражения. - person Robert Schmidt; 20.09.2018
comment
@RobertSchmidt Спасибо за разъяснения. Я отредактирую, чтобы более точно указать это. - person Tim; 21.09.2018