В недавней статье описывались некоторые проблемы при работе с интерфейсами Java, исходящие из языка, который допускает утиный ввод.
Суть вот в чем. Есть интерфейс:
public interface DynamoImpl { public PutItemResult putItem(PutItemRequest putItemRequest); public GetItemResult getItem(GetItemRequest getItemRequest); }
И есть класс Amazon DynamoDB
. Этот класс имеет два вышеуказанных метода, но также и многое другое. Теперь Джон, программист, хочет предоставить только эти два метода Amazon DynamoDB
остальной части своей программы. Он хочет использовать их в своем классе База данных
:
public class Database { public DynamoImpl db; // code removed for simplicity public Database(DynamoImpl db) { this.db = db; } }
Теперь: на языке, поддерживающем утиный ввод, вы могли бы просто передать экземпляр class Amazon DynamoDB
в конструктор и вызовите два метода (потому что они есть в классе). Но только не на Яве. Вам нужно передать экземпляр класса, который буквально реализует
интерфейс
DynamoImpl (или расширяет класс, который делает это). Но
Amazon DynamoDB
Самый простой способ достичь той же цели в Java – создать оболочку, которая делегирует экземпляр Amazon DynamoDB |/:
public class AmazonDynamoDbWrapper implements DynamoImpl{ private AmazonDynamoDB amazonDynamoDB; public AmazonDynamoDbWrapper(AmazonDynamoDB amazonDynamoDB) { this.amazonDynamoDB = amazonDynamoDB; } @Override public PutItemResult putItem(PutItemRequest putItemRequest) { return amazonDynamoDB.putItem(putItemRequest); } @Override public GetItemResult getItem(GetItemRequest getItemRequest) { return amazonDynamoDB.getItem(getItemRequest); } }
Да, немного многословно, но довольно просто, как только вы поймете шаблон. Теперь вы можете передать экземпляр этого класса экземпляру Database
.
Вот и все для производственного кода. А как насчет тестов?
Самое простое решение – обеспечить минимальную реализацию интерфейса Dynamo Impl
, например:
public class StubbedDynamoImpl implements DynamoImpl { @Override public PutItemResult putItem(PutItemRequest putItemRequest) { return new PutItemResult(/* Test data here */); } @Override public GetItemResult getItem(GetItemRequest getItemRequest) { return new GetItemResult(/* Test data here */); } }
Опять же, вы можете передать экземпляр этого класса в Опять же, вы можете передать экземпляр этого класса в
public class DatabaseTest { @Test public void createsDatabase() { Database database = new Database(new StubbedDynamoImpl()); // Whatever your test assertions are, here } // ... }
Нет необходимости в причудливых издевательских фреймворках, если все, что вы хотите показать, – это два метода. Это самое простое решение, которое я мог придумать. Я надеюсь, что это полезно.
P.S. Одно слово относительно соглашений Java: пожалуйста, не используйте суффикс Impl для интерфейсов. Это должно использоваться для конкретных классов реализации (если вообще используется), иначе вы можете запутать многих людей.
Оригинал: “https://dev.to/bertilmuth/why-java-interfaces-arent-terrible-just-strict-3de2”