以这本书为例,假设我有一个带有以下API的图书资源(还有更新和删除等,但为了简单起见,这里只显示两个)

GET /book/{id}
POST /book

每个API都会调用其他API来获得结果,而不是典型的CRUD数据库操作.根据需求/现有框架约束,有两个单独的控制器类GetBookControllerCreateBookController.控制器负责处理请求和响应.因此,实际的业务逻辑和检索/创建图书都在服务层.

接下来的问题是,是应该为每一个book操作(GetBookServiceCreateBookService)提供一个单独的接口,还是只有一个接口(BookService)?

基于接口隔离原则,该原则规定"不应强迫客户依赖他们不使用的接口".在这里,GetBookController类是客户机,它只需要查询book而不创建book,因此只需要GetBookService.如果使用BookService,则不使用方法createBook,这似乎违反了ISP原则.然而,如果使用单独的接口,将导致创建许多接口和实现类.我是否误解了ISP原则?

@Controller
public class GetBookController {
    
    @Autowired
    private GetBookService getBookService;
}

@Controller
public class CreateBookController {
    
    @Autowired
    private CreateBookService createBookService;
}

public interface GetBookService {
    Book getBook(long id);
}

public interface CreateBookService {
    Boolean createBook(Book request);
}

public interface BookService {
    Book getBook(long id);
    Boolean createBook(Book request);
}

推荐答案

it would result in many interface and implementation classes being created

是的,你说得对

The question then is, should there be a separate interface for each of book operation(GetBookService and CreateBookService), or to have just only one (BookService)?

ISP是如何使用接口的.所以,是否有必要使用其他接口方法,取决于您需要的更多.

使用ISP的HDD示例如下:

public interface IReadable
{
    string Read();
}

public interface IWriteable
{
    void Write();
}

public class HDD : IReadable, IWriteable
{
    public string Read() { }

    public void Write()  { }
}

通过为Read()个和Write()个方法创建一个接口,类将有义务在类中实现both个方法.但有些类只想读取数据,有些类想写入数据,有些类则两者兼而有之.例如,读卡器可能想要读取数据.因此,在这种情况下,最好创建单独的接口.

让我们看另一个CardReader的例子.CardReader只读取数据,不写入数据.所以,如果我们继承了一个包含Read()个和Write()个方法的接口,那么我们就违反了ISP原则.违反ISP的一个例子:

public interface IWriteReadable
{
    string Read();
    void Write();
}

public class CardReader : IWriteReadable
{
    // this is a necessary method
    public string Read() { }

    // here we are obligating to implement unnecessary method of interface
    public void Write() { }
}

所以,通过应用ISP,您只在接口中放入客户端类所需的方法.如果您的类/客户机只想读取数据,那么您需要使用IReadable接口,而不是IReadableWriteable接口.

在我看来,就像reply建议的那样,最好为book创建一个控制器.如果这些方法有很多变体,并且控制器变得非常大,那么可以为读取和创建操作使用单独的控制器.

Java相关问答推荐

最小拓Flutter 排序的时间复杂度是多少?

如何在SystemiccationRetryListenerSupport中获得类级别的spring retryable annotation中指定的标签?

Quarkus keycloat配置不工作.quarkus. keycloak. policy—enforcer. enable = true在. yaml表示中不工作

使用JdkClientHttpRequestFactory通过Spring RestClient和Wiemock读取时达到EOF

无法在WebSocket onMessage中捕获错误

对某一Hyroby控制器禁用@cacheable

Spring Data JPA慢慢地创建了太多非活动会话

解释左移在Java中的工作原理

在Java 17中使用两个十进制数字分析时间时出错,但在Java 8中成功

如何从Keyloak映射Hibernate实体中的用户

来自外部模块的方面(对于Java+Gradle项目)不起作用

如何用内置Java从JavaFX应用程序中生成.exe文件?

在Spring Boot应用程序中,server.port=0的默认端口范围是多少?

Java中HashSet的搜索时间与TreeSet的搜索时间

在不使用instanceof或强制转换的情况下从父类变量调用子类方法

在ECLIPSE上的M1 Pro上运行JavaFX的问题

Android上的SQLite:Android.database.SQLite.SQLiteReadOnlyDatabaseException:try 写入只读数据库(代码1032 SQLite_readonly_DBMOVED)

使用StringBuilder和append方法创建字符串时Java字符串内部方法的问题

[Guice/MissingImplementation]:未绑定任何实现

UuidGenerator Bean 类型不匹配?