С какой буквы желательно начинать имена интерфейсов
Перейти к содержимому

С какой буквы желательно начинать имена интерфейсов

  • автор:

Интерфейсы (F#)

Интерфейсы указывают наборы связанных элементов, которые реализуют другие классы.

Синтаксис

// Interface declaration: [ attributes ] type [accessibility-modifier] interface-name = [ interface ] [ inherit base-interface-name . ] abstract member1 : [ argument-types1 -> ] return-type1 abstract member2 : [ argument-types2 -> ] return-type2 . [ end ] // Implementing, inside a class type definition: interface interface-name with member self-identifier.member1argument-list = method-body1 member self-identifier.member2argument-list = method-body2 // Implementing, by using an object expression: [ attributes ] let class-name (argument-list) = < new interface-name with member self-identifier.member1argument-list = method-body1 member self-identifier.member2argument-list = method-body2 [ base-interface-definitions ] >member-list 

Remarks

Объявления интерфейса похожи на объявления классов, за исключением того, что члены не реализованы. Вместо этого все члены являются абстрактными, как указано в ключевое слово abstract . Текст метода не предоставляется для абстрактных методов. F# не может определить реализацию метода по умолчанию в интерфейсе, но она совместима с реализациями по умолчанию, определенными в C#. Реализации по умолчанию, использующие default ключевое слово, поддерживаются только при наследовании от базового класса, отличного от интерфейса.

По умолчанию для интерфейсов используется специальные public возможности .

При необходимости можно присвоить каждому параметру метода имя с помощью обычного синтаксиса F#:

type ISprintable = abstract member Print: format: string -> unit 

В приведенном выше ISprintable примере Print метод имеет один параметр типа string с именем format .

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

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

Стиль программирования .NET заключается в том, чтобы начать все интерфейсы с заглавной буквы I .

Можно указать несколько параметров двумя способами: F#style и . В стиле NET. Оба варианта компилируются одинаково для потребителей .NET, но F#-style принудит вызывающие объекты F# использовать приложение параметров F#style и . В стиле NET вызывающие объекты F# будут использовать приложение аргументов с кортежем.

type INumericFSharp = abstract Add: x: int -> y: int -> int type INumericDotNet = abstract Add: x: int * y: int -> int 

Реализация интерфейсов с помощью типов классов

Вы можете реализовать один или несколько интерфейсов в типе класса, используя interface ключевое слово, имя интерфейса и with ключевое слово, а затем определения элементов интерфейса, как показано в следующем коде.

type IPrintable = abstract member Print: unit -> unit type SomeClass1(x: int, y: float) = interface IPrintable with member this.Print() = printfn "%d %f" x y 

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

Методы вызывающего интерфейса

Методы интерфейса можно вызывать только через интерфейс, а не через какой-либо объект типа, реализующий интерфейс. Таким образом, для вызова этих методов может потребоваться выполнить преобразование к типу интерфейса с помощью :> оператора или upcast оператора .

Чтобы вызвать метод интерфейса при наличии объекта типа SomeClass , необходимо включить объект в тип интерфейса, как показано в следующем коде.

let x1 = new SomeClass1(1, 2.0) (x1 :> IPrintable).Print() 

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

type SomeClass2(x: int, y: float) = member this.Print() = (this :> IPrintable).Print() interface IPrintable with member this.Print() = printfn "%d %f" x y let x2 = new SomeClass2(1, 2.0) x2.Print() 

Реализация интерфейсов с помощью выражений объектов

Выражения объектов предоставляют короткий способ реализации интерфейса. Они полезны, если вам не нужно создавать именованный тип, а требуется просто объект, поддерживающий методы интерфейса, без каких-либо дополнительных методов. Выражение объекта показано в следующем коде.

let makePrintable (x: int, y: float) = < new IPrintable with member this.Print() = printfn "%d %f" x y >let x3 = makePrintable (1, 2.0) x3.Print() 

Наследование интерфейса

Интерфейсы могут наследовать от одного или нескольких базовых интерфейсов.

type Interface1 = abstract member Method1: int -> int type Interface2 = abstract member Method2: int -> int type Interface3 = inherit Interface1 inherit Interface2 abstract member Method3: int -> int type MyClass() = interface Interface3 with member this.Method1(n) = 2 * n member this.Method2(n) = n + 100 member this.Method3(n) = n / 10 

Реализация интерфейсов с реализациями по умолчанию

C# поддерживает определение интерфейсов с реализациями по умолчанию, например:

using System; namespace CSharp < public interface MyDim < public int Z =>0; > > 

Эти данные можно использовать напрямую из F#:

open CSharp // You can implement the interface via a class type MyType() = member _.M() = () interface MyDim let md = MyType() :> MyDim printfn $"DIM from C#: %d" // You can also implement it via an object expression let md' = < new MyDim >printfn $"DIM from C# but via Object Expression: %d" 

Вы можете переопределить реализацию по умолчанию с override помощью , например переопределения любого виртуального члена.

Все члены в интерфейсе, не имеющие реализации по умолчанию, по-прежнему должны быть реализованы явным образом.

Реализация одного интерфейса в разных универсальных экземплярах

F# поддерживает реализацию одного интерфейса в разных универсальных экземплярах, например:

type IA = abstract member Get : unit -> 'T type MyClass() = interface IA with member x.Get() = 1 interface IA with member x.Get() = "hello" let mc = MyClass() let iaInt = mc :> IA let iaString = mc :> IA iaInt.Get() // 1 iaString.Get() // "hello" 

См. также

  • Справочник по языку F#
  • Выражения объекта
  • Классы

Совместная работа с нами на GitHub

Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.

С какой буквы желательно начинать имена интерфейсов

name() возвращает имя объекта; name(aString) задает имя объекта — aString.

  • В описании операции должно быть указано, что она делает, включая ключевые алгоритмы, и какое значение она возвращает.
  • Называйте параметры операции так, чтобы указать, какая информация будет передана операции.
  • Укажите тип параметра.

Документальное оформление интерфейсов

Поведение, определенное интерфейсом, задано в виде набора операций. Может потребоваться дополнительная информация:

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

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

Советы и рекомендации

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

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

При создании интерфейса придерживайтесь следующих рекомендаций:

  • Используйте нотацию lollipop, если нужно просто указать наличие стыка в системе. В большинстве случаев это потребуется для подсистем, но не для классов.
  • Используйте расширенную нотацию class, если нужно описать детали самой службы. В большинстве случаев это потребуется для указания служб пакета или подсистемы.

© Copyright IBM Corp. 1987, 2006. Все права защищены..

С какой буквы желательно начинать имена интерфейсов

Интерфейс

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

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

Вернувшись к примеру из первого абзаца мы теперь можем вполне аргументированно объяснить почему GUI так часто приравнивают к слову интерфейс: он просто является частным случаем интерфейса между приложением и его пользователем. Можно было бы привести еще массу примеров различных интерфейсов, скажем сокет в качестве интерфейса между процессором и материнской платой, но целью написания этого поста было вовсе не это.

Уже догадались? Да, это я так неспеша плавно подводил разговор к объектно-ориентированному программированию. Термин интерфейс широко применяется и в нем. Как не трудно предположить, в роли объектов в этом случае выступают как сами классы, так и их экземпляры (которые, впрочем, тоже принято называть словом объект).

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

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

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

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

  • Ключевое слово interface обозначает описание интерфейса;
  • За ним следует название конкретного интерфейса, которое впоследствии можно будет использовать в коде при его упоминании (некоторые программисты на правах традиции начинают названия интерфейсов с заглавной буквы I, мне в свое время даже пытались объяснить зачем так надо делать, но аргументы не показались мне достаточно весомыми);
  • Далее идет тело интерфейса, в котором перечисляются все заголовки методов, которые должны быть в классе, реализующем данный интерфейс (никакой реализации!);
  • Впоследствии приписав к заголовку любого класса ключевое слово implements с последующим указанием названия интерфейса, можно обязать этот класс реализовать указанные в описания интерфейса методы. Существует небольшое исключение для абстрактных классов (то есть классов,для которых не может быть создан объект, обозначаются ключевым словом abstract ), они могут и не реализовать все методы интерфейса, но тогда эта обязанность будет переложена на их наследников.

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

Небольшое примечание: сами интерфейсы и методы в их теле по-умолчанию обладают свойствами abstract и public , так что повторно указывать эти ключевые слова не нужно.

На практике же это выглядит это примерно следующим образом:

// описание интерфейса interface Renderable  // обязуем реализовать метод draw public void draw(); > // конкретная реализация интерфейса class SomeText implements Renderable  string text; public SomeText(string str)  this.text=str; > public void draw()  // вынуждены подчиниться и реализовать System.out.println(this.text); > > // класс-клиент class Render  public Render(Renderable obj)  // можно быть уверенным, что // метод draw реализован obj.draw(); /* в качестве альтернативы можно было бы написать как-то так: if(obj instanceof Renderable)obj.draw(); то есть проверить реализован ли интерфейс вместо использования его названия в роли типа данных */ > 

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

Как я уже упоминал: альтернативой такому подходу является использование полиморфизма и наследования. Такой подход более распространен в других языках программирования, например C++, но пример я приведу все равно на Java, основываясь на предыдущем примере, чтобы читателям было проще сравнивать.

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

// теперь используем абстрактный класс abstract class Renderable  // реализуем метод draw public void draw()  System.out.println("Вывод на экран недоступен!"); > > // реализация интерфейса (на этот раз неформального) class SomeText extends Renderable  // на этот раз используем extends (наследование) // вместо implements string text; public SomeText(string str)  this.text=str; > public void draw()  // переопределяем метод draw // но могли этого и не делать, тогда // использовался бы метод из Renderable System.out.println(this.text); > > // класс-клиент class Render  public Render(Renderable obj)  // можно быть уверенным, что // метод draw реализован obj.draw(); /* на этот раз так как в крайнем случае в крайнем случае вызовется хотябы метод из класса Renderable */ > 

Минимальные изменения — суть та же. Сразу хочу отметить, что этот процесс так прост только в Java, в других языках программирования понадобилось бы использование дополнительных модификаторов для метода draw (например в C#: virtual или abstract в классе-потомке и override в классе-наследнике, это необходимо для обеспечения возможности их переопределения).

На этом позвольте завершить данное повествование, очень надеюсь, что мне удалось изложить суть максимально прозрачно. Эта тема будет активно подниматься в дальнейших статьях по ООП, так что очень надеюсь, что она стала для Вас элементарной и очевидной. По традиции напоминаю, что не пропустить публикацию новых постов можно подписавшись на RSS.

Интерфейсы

Интерфейс представляет ссылочный тип, который может определять некоторый функционал — набор методов и свойств без реализации. Затем этот функционал реализуют классы и структуры, которые применяют данные интерфейсы.

Определение интерфейса

Для определения интерфейса используется ключевое слово interface . Как правило, названия интерфейсов в C# начинаются с заглавной буквы I , например, IComparable, IEnumerable (так называемая венгерская нотация), однако это не обязательное требование, а больше стиль программирования.

Что может определять интерфейс? В целом интерфейсы могут определять следующие сущности:

  • Методы
  • Свойства
  • Индексаторы
  • События
  • Статические поля и константы (начиная с версии C# 8.0)

Однако интерфейсы не могут определять нестатические переменные. Например, простейший интерфейс, который определяет все эти компоненты:

interface IMovable < // константа const int minSpeed = 0; // минимальная скорость // статическая переменная static int maxSpeed = 60; // максимальная скорость // метод void Move(); // движение // свойство string Name < get; set; >// название delegate void MoveHandler(string message); // определение делегата для события // событие event MoveHandler MoveEvent; // событие движения >

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

Методы и свойства интерфейса могут не иметь реализации, в этом они сближаются с абстрактными методами и свойствами абстрактных классов. В данном случае интерфейс определяет метод Move, который будет представлять некоторое передвижение. Он не имеет реализации, не принимает никаких параметров и ничего не возвращает.

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

Модификаторы доступа

Еще один момент в объявлении интерфейса: если его члены — методы и свойства не имеют модификаторов доступа, то фактически по умолчанию доступ public , так как цель интерфейса — определение функционала для реализации его классом. Это касается также и констант и статических переменных, которые в классах и структурах по умолчанию имееют модификатор private. В интерфейсах же они имеют по умолчанию модификатор public. И например, мы могли бы обратиться к константе minSpeed и переменной maxSpeed интерфейса IMovable:

Console.WriteLine(IMovable.maxSpeed); // 60 Console.WriteLine(IMovable.minSpeed); // 0

Но также, начиная с версии C# 8.0, мы можем явно указывать модификаторы доступа у компонентов интерфейса:

interface IMovable < public const int minSpeed = 0; // минимальная скорость private static int maxSpeed = 60; // максимальная скорость public void Move(); protected internal string Name < get; set; >// название public delegate void MoveHandler(string message); // определение делегата для события public event MoveHandler MoveEvent; // событие движения >

Как и классы, интерфейсы по умолчанию имеют уровень доступа internal , то есть такой интерфейс доступен только в рамках текущего проекта. Но с помощью модификатора public мы можем сделать интерфейс общедоступным:

public interface IMovable

Реализация по умолчанию

Также начиная с версии C# 8.0 интерфейсы поддерживают реализацию методов и свойств по умолчанию. Это значит, что мы можем определить в интерфейсах полноценные методы и свойства, которые имеют реализацию как в обычных классах и структурах. Например, определим реализацию метода Move по умолчанию:

interface IMovable < // реализация метода по умолчанию void Move() < Console.WriteLine("Walking"); >>

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

interface IMovable < // реализация метода по умолчанию void Move() =>Console.WriteLine("Walking"); // реализация свойства по умолчанию // свойство только для чтения int MaxSpeed < get < return 0; >> >

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

Console.WriteLine(IMovable.MaxSpeed); // 60 IMovable.MaxSpeed = 65; Console.WriteLine(IMovable.MaxSpeed); // 65 double time = IMovable.GetTime(500, 10); Console.WriteLine(time); // 50 interface IMovable < public const int minSpeed = 0; // минимальная скорость private static int maxSpeed = 60; // максимальная скорость // находим время, за которое надо пройти расстояние distance со скоростью speed static double GetTime(double distance, double speed) =>distance / speed; static int MaxSpeed < get =>maxSpeed; set < if (value >0) maxSpeed = value; > > >

Добавление интерфейса

Стоит отметить, что в Visual Studio есть специальный компонент для добавления нового интерфейса в отдельном файле. Для добавления интерфейса в проект можно нажать правой кнопкой мыши на проект и в появившемся контекстном меню выбрать Add -> New Item. и в диалоговом окне добавления нового компонента выбрать пункт Interface :

Интерфейсы в C#

Хотя мы также может добавить стандартный файл класса или любой другой файл кода C# и в нем определить интерфейс.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *