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

#computer-science #decorator #theory #traits #aspect

#информатика #декоратор #теория #Трейты #аспект

Вопрос:

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

  • Декоратор
  • Атрибут
  • Аспект
  • Признак

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

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

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

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

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

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

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

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

Комментарии:

1. Между языками нет стандарта. Кроме того, я не думаю, что все языки поддерживают строгое определение каждого из них. Возьмем Java. Декоратор = аннотация, атрибут = Поле, признак = метод по умолчанию (возможно, интерфейс)… С другой стороны, аспект — это скорее концепция архитектуры программного обеспечения, чем особенность какого-либо одного языка.

2. Тогда само собой разумеется, что для каждой из этих связанных «вещей» должен существовать «термин» на уровне парадигмы. Мне было бы трудно поверить, что с теоретической точки зрения вы не смогли бы так точно определить их количество.

3. Как часто вам действительно нужно переключаться между языками программирования, чтобы обсудить концепции, а не фактическую терминологию языков? Страница Википедии, посвященная признаку и аспекту, кажется адекватной в моем понимании с высокого уровня. (Ссылка: en.m.wikipedia.org/wiki/Aspect_ (computer_programming) ) Но, если вы говорите о Python, а я говорю о C #, и мы оба говорим декораторы, тогда наше понимание, вероятно, отличается.

4. Я не уверен, насколько это актуально. Вопрос касается теории и информатики. Но ответ часто. Я имею дело с множеством разных технологий.

5. @MichaelScheper Аннотация напрямую не изменяет поведение или не добавляет новое поведение, в то время как декоратор это делает. Аннотации могут косвенно изменять поведение, но аннотация — это, по сути, просто метаданные, дополнительная информация о классе.

Ответ №1:

Декоратор

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

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

 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");
  

или

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

Атрибут

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

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

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

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

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

Аспект

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

Не могли бы вы выполнить некоторые из тех же действий, что и декораторы и атрибуты, используя аспекты? Конечно. Но зачем вам создавать себе такую головную боль? AOP — это как следующий шаг от ООП, только его следует использовать при необходимости. Когда это необходимо? Когда у конкретного приложения много «сквозных проблем», таких как безопасность или ведение журнала — например, банковское приложение. Эти проблемы носят сквозной характер; они выходят за традиционные границы, которые создают хорошо определенные классы и пакеты. Когда вы ведете журнал, от этого мало пользы, если вы не регистрируете все; следовательно, эта проблема носит сквозной характер. Итак, когда вы переходите к обновлению механизма ведения журнала одного класса, было бы сложно одновременно модифицировать все другие классы и 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 вода здесь немного более мутная, но общая концепция та же (прочитайте ключевое слово «virtual», если хотите начать с кроличьей норы).

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

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

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

Комментарии:

1. ИМХО, черта выходит далеко за рамки интерфейса. Интерфейс только объявляет, какие методы могут быть найдены, но не предоставляет реализацию. Признаки также включают определение. Итак, я рассматриваю признак скорее как какой-то частичный класс , который может быть частичным для нескольких классов

2. @derM Как указано в моем ответе, разные языки имеют разные значения. Я согласен, что черты МОГУТ выходить далеко за рамки простого интерфейса. Я никогда не утверждал, что они не могут быть чем-то большим, чем это. Когда вы разбираетесь в том, что черты всегда означают, в сравнении с тем, что они могут иногда означать, общим знаменателем становится интерфейс. Я согласен, что здесь есть двусмысленность, но такова природа обсуждения. Не стесняйтесь публиковать свой собственный ответ, который лучше объясняет черты.

3. Обратите внимание на это. Сетевые атрибуты ассоциируются не с объектами (экземплярами), а с сущностями системы типов (например, типами, полями, свойствами, методами и параметрами). Я склонен рассматривать атрибуты как бессильные теги или метки, которые могут предоставить большую мощность компилятору, среде выполнения или пользовательскому коду с использованием отражения.

4. @RobertSchmidt Спасибо за разъяснение. Я отредактирую, чтобы более точно указать это.