Home > Java > javaTutorial > body text

How to better manage your objects in Java

little bottle
Release: 2019-04-30 15:11:54
forward
1945 people have browsed it

This article tells the method of managing objects in the form of storytelling, which has certain value. Friends who are interested can take a look. I hope it can make you have a deeper understanding of classes.

One night a question suddenly popped into my mind: "How to manage objects in our code".

Xiaoyi was me when I first started working. He said: Just create an object through new and use it directly.

public class HelloWorld {
public void hello() {
System.out.println("hello world!");
}
}
HelloWorld helloWorld = new HelloWorld();
helloWorld.hello();
Copy after login

Look, I have a HelloWorld class. I can directly create an object using new, and then I can use all the methods in this object. How simple.

Er Yi is me who has been working for two years. He said to Xiao Yi with a look of contempt, please don’t say Hello World all day, okay? Also, you don’t know anything except New, can you be a little bit Pursue?

Xiaoyi said to Eryi, what else can you do besides new?

Eryi said that you can create object instances through newInstance of Class or newInstance of Constructor.

But you have to remember that Class's newInstance can only instantiate objects for those classes that have visible (Accessible) no-argument constructors, and Constructor does not have these restrictions.

Da Yi, who has been working for three years, said that although your methods can be used to create objects, they are all created manually, which is too primitive and the productivity is too low.

If a worker wants to do his job well, he must first sharpen his tools, and we must also find an efficient productivity tool. Do you know about IOC containers?

In the past, when we wanted to call a method of another object in an object, we would manually create the object through new or reflection, but doing this every time was too tiring, and the coupling between classes Also very high.

Through the IOC container, we can hand over all objects to the container for management. We only need to define the object before using it, and then when the object is used, the IOC container will help us initialize the object. , isn’t this more convenient?

After Da Yi finished speaking, he gave an example:

@Bean
public class RegisterService {
public void register() {
// do register
}
}
@Bean
public class LoginService {
public void login() {
// do login
}
}
@Bean
public class HelloWorld {
@Autowired
private RegisterService registerService;
@Autowired
private LoginService loginService;
public void hello() {
// 注册
registerService.register();
// ...
// 登录
loginService.login();
}
}
Copy after login

The IOC container uses an annotation called Bean to scan all classes annotated by Bean when the system starts, and instantiates these classes. ization, and then save all objects in the container. Then scan all properties or methods marked by Autowired, find matching objects (by name or type, etc.) from the container, and assign specific objects to these properties. In this way, we can use these objects directly. Isn't it very happy to be a hand-in-hand party?

Lao Yi is me who has been working for five years. After listening to Da Yi’s words, he raised a question. This kind of IOC container can be used for new projects, but for those legacy old projects, It is not realistic to use IOC for transformation.

Let me give you an example. In an old legacy project, there is a core interface Handler:

public interface Handler<REQ, RES> {
    RES handle(REQ request);
}
Copy after login

The Handler interface has many implementation classes. We need to call different ones for different requests. Handler implementation classes are used for processing. If IOC containers are used to manage these implementation classes, it is obviously not suitable because we do not know which Handler implementation class to use before processing.

Da Yi thought about it, if the Handler interface only has a few fixed implementation classes, and only one will be used for processing, then it can be determined through configuration before startup. A kind of Handler, for example, you can use @Conditional to determine the specific object to be loaded according to certain conditions, but it is really tricky to determine the type of the Handler object when it is used.

Seeing that everyone was silent, Lao Yi continued.

In order to use different Handlers to handle different requests when calling methods, it is necessary to determine two classes, one is the request class and the other is the processing class, and the request class and processing class must be made one by one. Correspond.

Assume that our request class is a Packet class, and each specific request class inherits from this base class.

So if you want to determine the type of each specific Packet, there are many ways. You can give each Packet a unique name, for example:

public abstract class Packet {
    public abstract String name();
}
Copy after login

You can also give each Packet a unique name. A Packet specifies a flag, for example:

public abstract class Packet {
    public abstract int symbol();
}
Copy after login

But no matter which way, each Packet implementation class needs to implement the method in the abstract class to "mark" what kind of Packet it is.

We take the second way as an example, assuming we have two specific Packets:

public class RegisterPacket extends Packet {
// 注册所需要的其他参数
int symbol() {
return 1;
}
}
public class LoginPacket extends Packet {
// 登录所需要的其他参数
int symbol() {
return 2;
}
}
Copy after login

In this way, when we receive the request object, we will know the request by calling request.symbol() Which type of Packet is it? At this time, you only need to find the specific Handler implementation class to handle it.

The request class can be determined, how to determine the Handler processing class? Can we also define a symbol method in the Handler interface, like this:

public interface Handler<REQ, RES> {
    int symbol();
    RES handle(REQ request);
}
Copy after login

In this case, as long as the symbol method is implemented in all implementation classes to mark what kind of request the Handler is used to handle, that is Can.

public RegisterHandler implements Handler<RegisterPacket, RES> {
    int symbol(){
    return 1;
    }
    RES handle(RegisterPacket request){
    // 具体的处理方法
    }
}
public LoginHandler implements Handler<LoginPacket, RES> {
    int symbol(){
    return 2;
    }
    RES handle(LoginPacket request){
    // 具体的处理方法
    }
}
Copy after login

Finally, instantiate all Handler implementation classes and save them in a HandlerProvider. When you want to use them, you can get them from the HandlerProvider:

public interface HandlerProvider {
    Handler getHandler(int symbol);
}
Copy after login

How to get all Handlers As for the implementation class, there are two methods.

一种是通过 ServiceLoader.load(Handler.class) 的方式来获取,不过这种通过 spi 的方式需要在项目的 resources/META-INF/services/ 目录下创建一个 xxx.Handler 的文件,并在文件中将所有 Handler 的实现类的完全类限定符列出来。

另一种比较简单的方式是通过扫描的方式,获取到所有 Handler 的实现类。

到现在为止,我们的实现还算可以,但是有一个问题,那就是在 Handler 接口中我们增加了一个方法,这样做就对原来的代码进行了侵入。

为了让原来的代码保持不变,我们可以定义一个注解来标注在所有的 Handler 实现类上,比如这样:

@Symbol(1)
public RegisterHandler implements Handler<RegisterPacket, RES> {
    RES handle(RegisterPacket request){
    // 具体的处理方法
    }
}
@Symbol(2)
public LoginHandler implements Handler<LoginPacket, RES> {
    RES handle(LoginPacket request){
    // 具体的处理方法
    }
}
Copy after login

这样就将 Handler 的实现和标注进行了解耦了,也可以通过扫描 @Symbol 注解来获取到所有的 Handler 实现类,不过这样做的缺点就是假如我忘记对某个 Handler 实现类添加 @Symbol 注解,到时候就获取不到该 Handler 了。

大家听完老弈的话之后,都陷入了沉思,我靠,还可以这么玩,真有趣。

这时候现在的我,也就是逅弈,说了一句,如果我有一个接口,他只有几个固定的实现类,我不想搞那一套那么重的实现方式,但是我也需要动态的获取实现类来对请求进行处理,那我该怎么办呢?

比如我有一个序列化的接口,如下所示:

public interface Serializer {
    byte[] serialize(Packet packet);
}
Copy after login

然后只有五种具体的序列化的实现类,如下所示:

public class JdkSerializer implements Serializer {
@Override
    public byte[] serialize(Packet packet) {
    // 具体的序列化操作
    }
}
public class FastJsonSerializer implements Serializer {
@Override
    public byte[] serialize(Packet packet) {
    // 具体的序列化操作
    }
}
public class HessianSerializer implements Serializer {
@Override
    public byte[] serialize(Packet packet) {
    // 具体的序列化操作
    }
}
public class KryoSerializer implements Serializer {
@Override
    public byte[] serialize(Packet packet) {
    // 具体的序列化操作
    }
}
public class ProtoStuffSerializer implements Serializer {
@Override
    public byte[] serialize(Packet packet) {
    // 具体的序列化操作
    }
}
Copy after login

那么我们该怎么确定使用哪种序列化方式对参数 packet 进行序列化呢?

使用老弈刚刚说的那一套也确实能够实现,不过太麻烦了,又得对 Packet 定义 symbol,又得对 Hander 实现类进行标注,还得扫描所有的实现类。

我只有五个实现类,不需要搞那么麻烦的。

其实很简单,只需要定义一个枚举类,表示序列化的算法,然后对 Packet 增加一个 algorithm 方法用来表示,使用何种序列化算法,如下所示:

public enum SerializeAlgorithm {
    JDK((byte) 1),
    FAST_JSON((byte) 2),
    HESSIAN((byte) 3),
    KRYO((byte) 4),
    PROTO_STUFF((byte) 5);
    private byte type;
    SerializeAlgorithm(byte type) {
        this.type = type;
    }
}
public abstract class Packet implements Serializable {
public abstract byte algorithm();
}
Copy after login

然后定义一个 SerializerChooser 根据不同的算法选择不同的 Serializer 实现类即可:

public interface SerializerChooser {
    Serializer choose(byte algorithm);
}
Copy after login

因为根据算法是可以知道对应的序列化接口的,所以就没有必要去扫描了,直接把几种序列化的实现类枚举出来即可,对象的实例可以使用单例模式,如下所示:

public class DefaultSerializerChooser implements SerializerChooser {
    private DefaultSerializerChooser() {
    }
    public static SerializerChooser getInstance() {
        return Singleton.get(DefaultSerializerChooser.class);
    }
    @Override
    public Serializer choose(byte algorithm) {
        SerializeAlgorithm serializeAlgorithm = SerializeAlgorithm.getEnum(algorithm);
        switch (serializeAlgorithm) {
            case JDK: {
                return Singleton.get(JdkSerializer.class);
            }
            case FAST_JSON: {
                return Singleton.get(FastJsonSerializer.class);
            }
            case HESSIAN: {
                return Singleton.get(HessianSerializer.class);
            }
            case KRYO: {
                return Singleton.get(KryoSerializer.class);
            }
            case PROTO_STUFF: {
                return Singleton.get(ProtoStuffSerializer.class);
            }
            default: {
                return null;
            }
        }
    }
}
Copy after login

我说完后,大家又一次陷入了沉思,我知道大家都在思考,他们会在每一次思考中获得进步和成长,正如我在思考后得到成长一样。

小鸟总有一天会成长为老鸟,我还走在成长的路上。

The above is the detailed content of How to better manage your objects in Java. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:oschina.net
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Latest Articles by Author
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template