public abstract class Request
{
// each request has its own approval algorithm. Each has to implement this method
public abstract void Approve();
// refuse algorithm is common for all requests
public void Refuse() { }
// static helper
public static void CheckDelete(string status) { }
// common property. Used as a comment for any operation against a request
public string Description { get; set; }
// hard-coded dictionary of css classes for server-side markup decoration
public static IDictionary<string, string> CssStatusDictionary
}
public class RequestIn : Request
{
public override void Approve() { }
}
public class RequestOut : Request
{
public override void Approve() { }
}
Хорошим примером являются классы .NET Stream. Класс Stream включает базовую функциональность, которую реализуют все потоки, а затем конкретные потоки предоставляют конкретные реализации для фактического взаимодействия с вводом/выводом.
Основная идея состоит в том, чтобы иметь абстрактный класс для предоставления скелета и базового функциональность и просто позвольте конкретной реализации предоставить точные необходимые детали.
Предположим, у вас есть интерфейс с ... +20 методами, например, интерфейс List.
List {interface }
+ add( object: Object )
+ add( index:Int, object: Object )
+ contains( object: Object ): Bool
+ get( index : Int ): Object
+ size() : Int
....
Если кому-то нужно предоставить реализацию для этого списка, он должен каждый раз реализовывать методы +20.
Альтернативой может быть абстрактный класс, который уже реализует большинство методов, и позволить разработчику реализовать некоторые из них.
Например
Чтобы реализовать неизменяемый список, программисту нужно только расширить этот класс и предоставить реализации для методов get (int index) и size ()
AbstractList: List
+ get( index: Int ) : Object { abstract }
+ size() : Int { abstract }
... rest of the methods already implemented by abstract list
В этой ситуации: get
и size
- это абстрактные методы , которые разработчик должен реализовать. Остальной функционал может быть уже реализован.
EmptyList: AbstractList
{
public overrride Object Get( int index )
{
return this;
}
public override int Size()
{
return 0;
}
}
Хотя эта реализация может выглядеть абсурдной, было бы полезно инициализировать переменную:
List list = new EmptyList();
foreach( Object o: in list ) {
}
, чтобы избежать нулевых указателей.
Использовал его для самодельной версии тетриса, где каждый тип тетрамино был дочерним классом класса тетрамино.
Я не спец по C#. Не возражаете, если я использую Java? Принцип тот же. Я использовал эту концепцию в одной игре. Я рассчитываю значение брони у разных монстров по-разному. Полагаю, я мог бы заставить их отслеживать различные константы, но концептуально это гораздо проще.
abstract class Monster {
int armorValue();
}
public class Goblin extends Monster {
int armorValue() {
return this.level*10;
}
}
public class Golem extends Monster {
int armorValue() {
return this.level*this.level*20 + enraged ? 100 : 50;
}
}
Вы можете использовать абстрактный метод (вместо интерфейса) каждый раз, когда у вас есть базовый класс, который на самом деле содержит некоторый код реализации, но для него нет разумной реализации по умолчанию или несколько его методов:
public class ConnectionFactoryBase {
// This is an actual implementation that's shared by subclasses,
// which is why we don't want an interface
public string ConnectionString { get; set; }
// Subclasses will provide database-specific implementations,
// but there's nothing the base class can provide
public abstract IDbConnection GetConnection() {}
}
public class SqlConnectionFactory {
public override IDbConnection GetConnection() {
return new SqlConnection(this.ConnectionString);
}
}
public abstract class MyBaseController {
public void Authenticate() { var r = GetRepository(); }
public abstract void GetRepository();
}
public class ApplicationSpecificController {
public override void GetRepository() { /*get the specific repo here*/ }
}
Это просто какой-то фиктивный код, представляющий некоторый реальный код, который у меня есть (для краткости это просто пример кода)
У меня есть 2 приложения ASP MVC, которые делают довольно похожие вещи.Логика безопасности / сеанса (наряду с другими вещами) происходит одинаково в обоих.
Я абстрагировал базовую функциональность из обеих в новую библиотеку, которую они оба наследуют. Когда базовому классу нужны вещи, которые могут быть получены только из фактической реализации, я реализую их как абстрактные методы. Поэтому в приведенном выше примере мне нужно извлечь информацию о пользователе из БД для выполнения аутентификации в базовой библиотеке. Чтобы получить правильную БД для приложения, у меня есть абстрактный метод GetRepository
, который возвращает репозиторий для приложения. Отсюда база может вызвать какой-либо метод в репозитории, чтобы получить информацию о пользователе и продолжить проверку или что-то еще.
Когда необходимо внести изменения в аутентификацию, мне теперь нужно обновить только одну библиотеку, а не дублировать усилия в обеих. Короче говоря, если вы хотите реализовать некоторые функции, но не все, то абстрактный класс отлично работает. Если вы хотите реализовать нефункциональность, используйте интерфейс.
Использование абстрактного метода очень распространено при использовании шаблона метода шаблона . Вы можете использовать его для определения скелета алгоритма, а также для того, чтобы подклассы изменяли или уточняли определенные шаги алгоритма, не изменяя его структуру.
Взгляните на «реальный» пример со страницы doFactory Template Method Pattern .
Например, предположим, что у вас есть классы, соответствующие строкам в вашей базе данных. Возможно, вы захотите, чтобы эти классы считались равными, если их идентификаторы равны, потому что так работает база данных. Таким образом, вы можете сделать идентификатор абстрактным, потому что это позволит вам написать код, который использует идентификатор, но не реализовать его, прежде чем вы узнаете об идентификаторе в конкретных классах. Таким образом, вы избегаете реализации одного и того же метода equals во всех классах сущностей.
public abstract class AbstractEntity<TId>
{
public abstract TId Id { get; }
public override void Equals(object other)
{
if (ReferenceEquals(other,null))
return false;
if (other.GetType() != GetType() )
return false;
var otherEntity = (AbstractEntity<TId>)other;
return Id.Equals(otherEntity.Id);
}
}
Пример
namespace My.Web.UI
{
public abstract class CustomControl : CompositeControl
{
// ...
public abstract void Initialize();
protected override void CreateChildControls()
{
base.CreateChildControls();
// Anything custom
this.Initialize();
}
}
}