英文:
java - What benefits can delegation to private static methods have over private methods in a springboot webflux (functional program style)
问题
关于在Java Spring Boot WebFlux项目中使用私有静态方法的问题,我有一个问题。
首先让我展示一些代码:
这是一个Spring项目的基本代码结构:一个主类(Main class),一个位于config包下的配置类(Configuration class),一个位于repository包下的仓库类(Repository class),一个位于service包下的服务接口(Service interface)。
现在,让我们来看一下关于服务层实现的问题:首先,有一个版本,其中公共方法将所有计算委托给后续的私有静态方法,并将字段作为参数传递。(选项1)
与之相反,还可以在私有方法中直接使用非静态字段。(选项2)
(将它们放在一起)
问题:
选项1可以带来什么好处?
这是一个技术问题,不是基于观点的。我确信这不仅仅是代码风格的偏好。我看到越来越多的项目在Java WebFlux函数式编程的背景下采用选项1。
- 就编译时性能而言,选项1相对于选项2有什么优势或劣势?
- 就运行时性能而言,选项1相对于选项2有什么优势或劣势?
- 就线程安全性而言,选项1相对于选项2有什么优势或劣势?
- 就可测试性而言,选项1相对于选项2有什么优势或劣势?
- 就模拟(mock)而言,选项1相对于选项2有什么优势或劣势?
- 就可维护性而言,选项1相对于选项2有什么优势或劣势?
- 就可读性而言,选项1相对于选项2有什么优势或劣势?
- 请问选项1还有哪些其他优势或劣势?
谢谢
英文:
I have a question regarding the use of private static methods in the context of a java springboot webflux project.
Let me put some code first:
The straightforward codes of a spring project: a Main class, a Configuration class found under a config package, a Repository class found under the package repository, a Service interface found under the service package.
@SpringBootApplication
public class SomeApplication {
public static void main(String[] args) {
SpringApplication.run(SomeApplication.class, args);
}
}
@Configuration
public class SomeConfiguration {
@Value("${someConfiguration}")
private String someConfiguration;
public String getSomeConfiguration() {
return someConfiguration;
}
}
@Repository
public interface SomeRepository extends Reactive[...]Repository<String, String> { }
public interface SomeService {
Mono<String> someCompute(String string);
}
(The question is not on above)
Now, as the implementation of the service layer: First, a version where the public facing method is delegating all computations to subsequent private static methods, passing the fields as parameter. (option 1)
@Service
public class SomeServiceImpl implements SomeService {
private final SomeConfiguration someConfiguration;
private final SomeRepository someRepository;
private final WebClient webClient;
@Autowired
public SomeServiceImpl(SomeConfiguration someConfiguration, SomeRepository someRepository, WebClient webClient) {
this.someConfiguration = someConfiguration;
this.someRepository = someRepository;
this.webClient = webClient;
}
@Override
public Mono<String> someCompute(String string) {
Mono<String> s1Mono = sendHttpRequestToSomewhere(webClient);
Mono<String> s2Mono = s1Mono.map(x -> computeSomethingWithConfiguration(x, someConfiguration));
Mono<Object> s3Mono = s2Mono.map(x -> saveToSomewhere(x, someRepository));
return s3Mono.map(x -> computeSomethingElse(x));
}
private static Mono<String> sendHttpRequestToSomewhere(WebClient webClient) {
return webClient.get().retrieve().bodyToMono(String.class);
}
private static String computeSomethingWithConfiguration(String x, SomeConfiguration someConfiguration) {
return x + someConfiguration.getSomeConfiguration();
}
private static Mono<String> saveToSomewhere(String x, SomeRepository someRepository) {
return someRepository.save(x);
}
private static String computeSomethingElse(Object x) {
return x.toString();
}
}
As opposed to using the fields directly in a private but non-static method. (option 2)
@Service
public class SomeServiceImpl implements SomeService {
private final SomeConfiguration someConfiguration;
private final SomeRepository someRepository;
private final WebClient webClient;
@Autowired
public SomeServiceImpl(SomeConfiguration someConfiguration, SomeRepository someRepository, WebClient webClient) {
this.someConfiguration = someConfiguration;
this.someRepository = someRepository;
this.webClient = webClient;
}
@Override
public Mono<String> someCompute(String string) {
Mono<String> s1Mono = sendHttpRequestToSomewhere();
Mono<String> s2Mono = s1Mono.map(x -> computeSomethingWithConfiguration(x));
Mono<Object> s3Mono = s2Mono.map(x -> saveToSomewhere(x));
return s3Mono.map(x -> computeSomethingElse(x));
}
private Mono<String> sendHttpRequestToSomewhere() {
return webClient.get().retrieve().bodyToMono(String.class);
}
private String computeSomethingWithConfiguration(String x) {
return x + someConfiguration.getSomeConfiguration();
}
private Mono<String> saveToSomewhere(String x) {
return someRepository.save(x);
}
private String computeSomethingElse(Object x) {
return x.toString();
}
}
(putting them together)
Question:
What are the benefits option 1 can bring?
This is a technical question, not opinion based. I am sure there is more than just code style preferences. I am seeing more and more projects adopting option 1 in the context of java webflux functional programs.
-
What are the benefits in terms of compile time performance option 1 have or lack over option 2?
-
What are the benefits in terms of runtime performance option 1 have or lack over option 2?
-
What are the benefits in terms of thread safety option 1 have or lack over option 2?
-
What are the benefits in terms of testability option 1 have or lack over option 2?
-
What are the benefits in terms of mocks option 1 have or lack over option 2?
-
What are the benefits in terms of maintainability option 1 have or lack over option 2?
-
What are the benefits in terms of readability option 1 have or lack over option 2?
-
What are other benefits option 1 have or lack over option 2 please?
Thank you
答案1
得分: 1
我认为这个问题与WebFlux和Spring没有关系,而是与以函数式风格编写的任何代码有关(你在使用Java 8流时也会面临相同的情况),甚至与以“传统”命令式风格编写的代码有关:应该如何命名私有的静态方法或私有方法?
总体上,我只能看到私有静态方法和私有方法之间的一个区别:
私有非静态方法可以访问类的状态/依赖关系(非静态数据字段),而 私有静态 方法则不行
这意味着如果:
- 你只在类内部使用它们
- 你想强调该方法不依赖(读取不访问)所在对象的内部状态
相反地,如果你已经维护了状态/依赖关系,比如示例中的 WebClient
,那么使用 static
就意味着你需要将这个参数传递给方法。在我看来,这是不必要的:假设你不是只有一个参数,而是有3个参数要传递,并且你从源类的许多位置调用此方法。
除此之外,我没有看到太多的区别:
-
模拟(测试)是一样的,因为你无论如何都不会测试私有方法,而且你可以在两种情况下都通过使用构造函数注入或一些高级的mockito技术来模拟你的依赖关系。
-
性能损耗 - 大致相同,我想不出在使用一个方法而不是另一个方法的情况下会导致性能问题。
-
可维护性 - 同样取决于你的用例,如果方法访问内部状态,你最好使用非静态变体以减少混乱,如果你想强调使用不依赖状态的“util”方法 - 使用
private static
方法。 -
多线程 - 如果需要,你在这两种情况下都应该同步状态,方法本身是否有
static
修饰符与多线程没有任何关系。
我还找到了这个帖子,它对这个问题可能有用,同样适用于通用的编程风格(代码组织),不仅仅适用于WebFlux。
英文:
I don't think this question is related to WebFlux and to Spring in general, but rather to any code written in a functional style (you'll face the same with java 8 streams) and even to a code written in a "traditional" imperative style: what to call private static or private method?
In general I can see only one difference between private static methods and private methods:
private non-static methods can access the state/dependencies (non static data fields) of the class, whereas private static methods can't
This means that you might want to use private static methods
if:
- You only use them within the class
- You want to emphasize that the method doesn't depend (read doesn't access) the internal state of the object its defined in.
On the opposite, if you already maintain the state/dependency, like WebClient
in the example, then using the static
makes you passing this parameter to the method. In My Opinion this is unnecessary: what if you have not 1, but say 3 parameters to pass and you call this method from many places in the source class.
Other than that I don't see much difference:
-
Mocks (testing) is just the same because you don't test private methods anyway and you can mock your dependencies in both cases in a same way by using constructor injection or some advanced techniques of mockito.
-
Performance penalty - roughly the same, I can't think about the case where the usage of one method over another will be a source of performance issues.
-
Maintainability - again, depends on your usecase, if the method is accessing the internal state you better use non static variant to reduce the clutter, if you want to emphasize that you use a "util" method that doesn't depend on the state - use
private static
method. -
Multithreading - you should synchronize the state in both cases if its required, the fact that the method has
static
modifier by itself doesn't deal with multithreading at all.
I've also found this thread that can be useful for this question, again its applicable for general programming style (code organization) not only for webflux.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论