将接口隔离原理应用于服务层接口

发布于 2025-01-20 04:29:48 字数 1167 浏览 2 评论 0原文

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

GET /book/{id}
POST /book

这些 API 中的每一个都会调用其他 API 来获取结果,而不是典型的 CRUD数据库操作。并且基于需求/现有框架约束,有单独的两个控制器类GetBookControllerCreateBookController。控制器负责处理请求和响应。所以实际的业务逻辑和检索/创建书籍都在服务层。

那么问题是,每个图书操作是否应该有一个单独的接口(GetBookServiceCreateBookService),还是只有一个接口(BookService) >)?

基于接口隔离原则,该原则规定“客户端不应被迫依赖于他们不使用的接口”。这里的GetBookController类是客户端,它只需要查询书籍而不需要创建它,所以它只需要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);
}

Using the book as analogy, suppose I have a book resource with the following API (there are also update and delete etc but for simplicity only show two here)

GET /book/{id}
POST /book

Each of these API would call other APIs to get the result, rather than the typical CRUD database operation. And based on the requirement / existing framework constraint, there are separate two controller class GetBookController and CreateBookController. The controller is to handle the request and response. So the actual business logic and retrieve / create book are in the service layer.

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)?

Based on the Interface Segregation Principle which states "Clients should not be forced to depend upon interfaces that they do not use". Here the GetBookController class is the client, where it only has to query book without creating it, so it only requires GetBookService. If it is to use BookService, it doesn't use the method createBook, which seems to violate ISP principle. However, if using separate interface, it would result in many interface and implementation classes being created. Am I misunderstanding the ISP principle?

@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);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

各空 2025-01-27 04:29:48

因此,我通常对书籍这样的 CRUD 实体执行的操作是单个 BookControler。您不需要多个书籍控制器,因为它是一个控制器 - 它控制与书籍相关的事物。所以应该是一个类。

服务的逻辑又相同。一本图书服务接口就足够了。它处理书籍的需求并返回请求的结果。

所以,是的,如果为每个方法都制作一个接口,接口就会太多,随着项目规模的扩大,这并不容易处理。此外,它使得每个接口只做很少的事情(在某些情况下这是期望的,但不是这个)。

ISP 原理的一个很好的类比是:您不想让咖啡机需要实现如何打印纸张,而需要打印机来实现如何制作咖啡。但制作不同咖啡的咖啡机也在其范围之内。

 @Controller
 public class BookController {
     private final BookService bookService;

     @Autowired
      private BookController (BookService bookService) {
          this.bookService = bookService;
      }
  }
    
  public interface BookService {

       Book getBook(long id);
    
       Boolean createBook(Book request);
  }

例如,您可以使用构造函数中的自动装配,以便更轻松地进行模拟。

So, what I usually do for a CRUD entity like a book is a single BookControler. You do not need multiple book controllers, as it is a Controller - it controls things associated with books. So it should be one class.

Again the same logic for the service. One book service interface is sufficient. It deals with the needs of the books and returns the result of a request.

So, yes, if you make an interface for each method, there would be too many interfaces, which is not easy to deal with as the project scales. Also, it makes each interface do very little (which in some cases is desired, but not this one).

A good analogy for the ISP principle would be: You do not want to make a coffee machine need to implement how to print a paper, and a printer to implement how to make coffee. But a coffee machine making different coffees is in its scope.

 @Controller
 public class BookController {
     private final BookService bookService;

     @Autowired
      private BookController (BookService bookService) {
          this.bookService = bookService;
      }
  }
    
  public interface BookService {

       Book getBook(long id);
    
       Boolean createBook(Book request);
  }

And for example, you can use autowiring from the constructor, for easier mocking.

浮光之海 2025-01-27 04:29:48

这将导致创建许多接口和实现类

是的,你是对的

接下来的问题是,每个图书操作是否应该有一个单独的接口(GetBookService 和 CreateBookService),还是只有一个接口(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() 方法创建一个接口,类将强制实现这两个方法。类中的方法。但有些类只想读取数据,其他类只想写入数据,有些类则两者兼而有之。例如,读卡器可能想要读取数据。因此在这种情况下最好创建单独的接口。

那么让我们看一下 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

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

it would result in many interface and implementation classes being created

Yeah, you are right

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 is how interface can be consumed. So, whether it is necessary to consume other methods of interface or don’t depend on more than you need.

An example with HDD that uses ISP:

public interface IReadable
{
    string Read();
}

public interface IWriteable
{
    void Write();
}

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

    public void Write()  { }
}

By creating one interface for Read() and Write() methods, it would obligate class to implement both methods in class. But some classes only want to read data, others want to write data, and some to do both. E.g. card reader might want to read data. So in this case it is better to create separate interfaces.

So let's look another example with CardReader. CardReader just reads data, it does not write data. So, if we inherit one interface with Read() and Write() methods, then we would violate ISP principle. An example of violation of 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() { }
}

So by applying ISP, you only puts methods in interface that are necessary for the client class. If your class/client just wants to read data, then you need to use IReadable interface, not IReadableWriteable.

And in my view and like an reply suggested, it is better to create one controller for book. It might be okay to have separate controllers for Read and Create actions if there are many variations of these methods and controller became very big.

寻梦旅人 2025-01-27 04:29:48

您不应该设计bookService接口,就好像您知道它可能拥有的所有客户端一样。通常,应该只有一个BookService接口,其目的是提供对书籍的crud访问。

该规则的一个例外是,如果书籍服务支持不同的访问级别的不同客户端角色。如果这种情况,您可能会有一个仅提供R的BookReaderService,并且bookupdaterservice将其扩展并提供完整的crud。

现在,您可能需要编写您的客户端,以便它们不依赖他们不需要的操作(ISP)。在这种情况下,您将在客户端定义客户所需的接口,然后使用适配器根据适当的书籍服务来提供这些接口...但实际上在这种情况下是过分的。您的控制器已经已经适配器,因此添加另一个适配器层是多余的。

You should not be designing the BookService interface(s) as if you know about all clients that it might have. Usually there should be only one BookService interface, the purpose of which is to provide CRUD access to books.

One exception to this rule would be if the book service supported different client roles with different access levels. If this case you might have a BookReaderService that provides only R, and a BookUpdaterService that extends it and provides full CRUD.

Now, you might want to write your clients so that they didn't depend on operations that they don't need (ISP). In this case, you would define, on the client side, the interfaces that the clients want, and then use an adapter to provide those interfaces based on the appropriate book service... But really that is overkill in this case. Your controllers are already adapters, so adding another adapter layer is redundant.

思念绕指尖 2025-01-27 04:29:48

ISP 的原则目标是通过将软件拆分为多个独立的部分来减少所需更改的副作用和频率。

但问题是需要多小才能拆分软件组件。

在你的例子中,你过度应用了这个原则,例如假设你有一个类:

class Student {
String name;
Integer age;
ClassRoom classRoom;

.
.
.
setters ....
getters ....
}

并且一个开发人员认为最好将其分开,所以他最终为每个setter和getter创建单独的类(这显然是不必要的),

这里与根据您的情况,最好有一个 BookController 和一个 BookService 来涵盖所有必需的操作(创建、获取、更新、删除)。

除非您有不同类型的 BookService (例如 ElectronicBook 、 PaperBook ),否则您可以有不同的 BookService 接口实现。

the ISP principle goal is to reduce the side effects and frequency of required changes by splitting the software into multiple, independent parts.

but the question is how small enough is required to split the software component .

In your case you are over applying the principle , for example suppose you have class :

class Student {
String name;
Integer age;
ClassRoom classRoom;

.
.
.
setters ....
getters ....
}

and one dev think it better to split it up so he ended up with making separate classes for each setters and getters (which is clearly unnecessary )

the same here with your case it's better to have one BookController and one BookService that covers all required operations (create , get , update , delete).

unless you have a different type of BookService ( for example ElectronicBook , PaperBook) then you can have different implementation of BookService interface.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文