存储所有基本对象的类是正确的方法吗?

InS*_*iTy 0 java oop design-patterns software-design instantiation

这个问题是面向对象编程中的一个设计问题,我正在尝试寻找或思考最佳解决方案,并且不确定到目前为止我想到的解决方案。

问题:

我编写了一个名为 的类ComponentsHub,其想法是在其中存储运行程序所必需的所有主要对象。每个对象都是静态的、最终的和公共的。我使用公共访问修饰符来轻松通过静态导入访问对象或仅静态访问它。

package com.rtransfer.net.components;

import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Logger;

import com.rtransfer.net.system.StorageManager;

public class ComponentsHub {

    public static final Logger logger;
    
    public static final Server server;
        
    public static final StorageManager storageManager;

    public static final SecurityManager securityManager;
    
    public static final RequestForwarder requestForwarder;
    
    public static final Authenticator authenticator;
    
    public static final Uploader uploader;

    public static final ConnectionHandler connectionHandler;

    public static final Listener listener;
    
    static {
        logger = Logger.getLogger("rtransfer.net");
        
        try {
            FileHandler handler = new FileHandler("logs/logs.txt");
            logger.addHandler(handler);
        } catch (SecurityException | IOException e) {
            e.printStackTrace();
        }       
        server = new Server();
            
        storageManager = new StorageManager();

        securityManager = new SecurityManager();
        
        requestForwarder = new RequestForwarder();
        
        authenticator = new Authenticator();
        
        uploader = new Uploader();

        connectionHandler = new ConnectionHandler();

        listener = new Listener();
    }
}
Run Code Online (Sandbox Code Playgroud)

很明显,这个代码/设计存在一些问题(希望你能告诉我其中一些问题)。例如,不分离关注点、难以测试、不可扩展等等。我对这种架构感觉不太舒服,我想我已经创建了某种“上帝级”。

我想到的解决方案:

  • 创建将构建这些对象的类,例如构建器或工厂,因此我将从 ComponentsHub 中删除构建和初始化这些对象的责任。

  • 创建类来更明确地集中这些对象。或者换句话说,创建子 ComponentsHub。

我知道这是设计面向对象系统时非常常见的问题。我想解决这个问题,这样我就不必在我的软件的进一步开发中生活在噩梦中。

我提出的解决方案是否解决了部分问题?还有其他选择吗?如果您向我推荐合适的设计模式或其他技术来解决这个问题,我会很高兴。我应该如何以及在哪里创建对象并允许相当简单地访问它们?


评论和回答结果:

正如其中一条评论中所建议的,我实现了 ServiceLocator,它看起来比以前的想法要好得多。现在,我知道对于它是否是反模式存在分歧,我也了解它的缺点是什么,但如果没有相关框架,那将是相当困难和累人的(正如在一个答案中也提到的)使用传统的依赖注入。

public class InitialContext {
    
    public ServiceComponent lookup(String serviceName) {
        if (serviceName.equalsIgnoreCase(Listener.class.getSimpleName())) {
            return new Listener();
        } else if (serviceName.equalsIgnoreCase(ConnectionHandler.class.getSimpleName())) {
            return new ConnectionHandler();
        } else if (serviceName.equalsIgnoreCase(Uploader.class.getSimpleName())) {
            return new Uploader();
        } else if (serviceName.equalsIgnoreCase(Authenticator.class.getSimpleName())) {
            return new Authenticator();
        } else if (serviceName.equalsIgnoreCase(RequestForwarder.class.getSimpleName())) {
            return new RequestForwarder();
        } else if (serviceName.equalsIgnoreCase(SecurityManager.class.getSimpleName())) {
            return new SecurityManager();
        } else if (serviceName.equalsIgnoreCase(StorageManager.class.getSimpleName())) {
            return new StorageManager();
        } else if (serviceName.equalsIgnoreCase(Server.class.getSimpleName())) {
            return new Server();
        } else {
            return null;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)
public class ServiceCache {

    private Hashtable<String, ServiceComponent> services;
    
    public ServiceCache() {
        services = new Hashtable<>();
    }
    
    public ServiceComponent getService(Class<?> service) {
        return services.get(service.getSimpleName());
    }
    
    public void addService(ServiceComponent newService) {
        services.put(newService.getClass().getSimpleName(), newService);
    }
}
Run Code Online (Sandbox Code Playgroud)
package com.rtransfer.net.components;

import com.rtransfer.utils.InitialContext;
import com.rtransfer.utils.ServiceCache;

public class ServiceLocator {

    private static ServiceCache cache = new ServiceCache();
    
    public static ServiceComponent getService(Class<?> serviceClass) {
        ServiceComponent service = cache.getService(serviceClass);
        
        if (service != null)
            return service;
        InitialContext context = new InitialContext();
        
        service = context.lookup(serviceClass.getSimpleName());
        cache.addService(service);
        
        return service;
    }
}
Run Code Online (Sandbox Code Playgroud)

所以最后我决定使用依赖注入

Tho*_*mas 7

无论您构造这个单例对象,它仍然是一个单例(或单例集合)。它可能会在类之间产生意想不到的相互依赖性,并使它们难以单独测试或推理。

放入搜索引擎的神奇短语是“依赖注入”。该术语带有很多包袱,人们有时会将其与复杂的框架联系起来,但其核心原则非常简单:每个类不应自主地寻找其依赖项,而应要求使用该类的代码提供依赖关系。

不是这个:

class Dog {
    void bark() {
        SoundManager.emitSound("woof");
    }
}
Run Code Online (Sandbox Code Playgroud)

但是这个:

class Dog {
    private final SoundManager soundManager;

    public Dog(SoundManager soundManager) {
        this.soundManager = soundManager;
    }

    void bark() {
        this.soundManager.emitSound("woof");
    }
}
Run Code Online (Sandbox Code Playgroud)

这个原则沿着依赖链向上延伸:如果aKennel想要创建一个Dog对象,它还需要SoundManager调用者请求a 。

您可以想象,传递所有这些构造函数参数会变得很乏味,尤其是随着代码库的增长。这就是框架有用的地方。