Также как примечание:
CRTP может использоваться для реализации статического полиморфизма (который, как динамический полиморфизм, но без таблицы указателей виртуальных функций).
#pragma once
#include <iostream>
template <typename T>
class Base
{
public:
void method() {
static_cast<T*>(this)->method();
}
};
class Derived1 : public Base<Derived1>
{
public:
void method() {
std::cout << "Derived1 method" << std::endl;
}
};
class Derived2 : public Base<Derived2>
{
public:
void method() {
std::cout << "Derived2 method" << std::endl;
}
};
#include "crtp.h"
int main()
{
Derived1 d1;
Derived2 d2;
d1.method();
d2.method();
return 0;
}
Выход был бы:
Derived1 method
Derived2 method
Чтобы протестировать интерфейс с общими тестами независимо от реализации, вы можете использовать абстрактный тестовый пример, а затем создать конкретные экземпляры тестового примера для каждой реализации интерфейса.
Абстрактная (базовая) тестовый пример выполняет нейтральные для реализации тесты (т. е. проверяет договор интерфейса), в то время как конкретные тесты заботятся о создании экземпляра объекта для тестирования и выполняют любые тесты, специфичные для реализации.
Может создавать методы, которые принимают параметр типа IMyInterface и имеют реальные методы тестирования, просто вызывая эти методы, проходящие в разных конкретных классах.
Если вы используете NUnit, вы можете использовать Grensesnitt:
public interface ICanAdd {
int Add(int i, int j); //dont ask me why you want different adders
}
public class winefoo : ICanAdd {
public int Add(int i, int j)
{
return i + j;
}
}
interface winebar : ICanAdd {
void FooBar() ;
}
public class Adder1 : winebar {
public int Add(int i, int j) {
return i + j;
}
public void FooBar() {}
}
public class Adder2 : ICanAdd {
public int Add(int i, int j) {
return (i + 12) + (j - 12 ); //yeeeeeaaaah
}
}
[InterfaceSpecification]
public class WithOtherPlugins : AppliesToAll<ICanAdd>
{
[TestCase(1, 2, 3)]
[TestCase(-1, 2, 1)]
[TestCase(0, 0, 0)]
public void CanAddOrSomething(int x, int y, int r)
{
Assert.AreEqual(subject.Add(x, y), r);
}
[TestCase(1, 2, Result = 3)]
[TestCase(-1, 2, Result = 1)]
[TestCase(0, 0, Result = 0)]
public int CannAddOrSomethingWithReturn(int x, int y) {
return subject.Add(x, y);
}
}
Вы не тестируете интерфейс напрямую, но можете написать абстрактный класс, который проверяет контракт, который должен распространяться на конкретную реализацию. Тест конкретного класса затем расширил абстрактный класс
Если вы хотите использовать те же тесты для разных исполнителей вашего интерфейса, используя NUnit в качестве примера:
public interface IMyInterface {}
class A : IMyInterface { }
class B : IMyInterface { }
class C : IMyInterface { }
public abstract class BaseTest
{
protected abstract IMyInterface CreateInstance();
[Test]
public void Test1()
{
IMyInterface instance = CreateInstance();
//Do some testing on the instance...
}
//And some more tests.
}
[TestFixture]
public class ClassATests : BaseTest
{
protected override IMyInterface CreateInstance()
{
return new A();
}
[Test]
public void TestCaseJustForA()
{
IMyInterface instance = CreateInstance();
//Do some testing on the instance...
}
}
[TestFixture]
public class ClassBTests : BaseTest
{
protected override IMyInterface CreateInstance()
{
return new B();
}
}
[TestFixture]
public class ClassCTests : BaseTest
{
protected override IMyInterface CreateInstance()
{
return new C();
}
}