In diesem Artikel wird die Methode zum Verwalten von Objekten in Form von Geschichten beschrieben, die einen gewissen Wert hat. Interessierte Freunde können einen Blick darauf werfen. Ich hoffe, dass sie Ihnen ein tieferes Verständnis für den Unterricht vermitteln können.
Eines Nachts kam mir plötzlich eine Frage in den Sinn: „Wie verwaltet man Objekte in unserem Code?“
Xiao Yi war ich, als ich anfing zu arbeiten. Er sagte: Erstelle einfach ein Objekt durch Neu und verwende es direkt.
public class HelloWorld { public void hello() { System.out.println("hello world!"); } } HelloWorld helloWorld = new HelloWorld(); helloWorld.hello();
Schauen Sie, ich habe eine HelloWorld-Klasse, mit der ich direkt ein Objekt erstellen und dann alle Methoden in diesem Objekt verwenden kann.
Eryi, das bin ich, der seit zwei Jahren arbeitet. Er sagte mit einem verächtlichen Blick zu Xiaoyi: „Sag bitte nicht den ganzen Tag „Hallo Welt“, okay?“ Außer „Neu“ weißt du nichts Kannst du sonst ein bisschen verfolgen?
Xiaoyi sagte zu Eryi, was kannst du außer Neuem noch tun?
Eryi sagte, dass Sie Objektinstanzen über newInstance of Class oder newInstance of Constructor erstellen können.
Aber Sie müssen bedenken, dass die newInstance der Klasse nur Objekte für die Klassen instanziieren kann, die über sichtbare (zugängliche) parameterlose Konstruktoren verfügen, während für den Konstruktor diese Einschränkungen nicht gelten.
Dayi, der seit drei Jahren für mich arbeitet, sagte, dass Ihre Methoden zwar zum Erstellen von Objekten verwendet werden können, diese jedoch alle manuell erstellt werden, was zu primitiv und die Produktivität zu gering ist.
Wenn ein Arbeiter seine Arbeit gut machen will, muss er zuerst seine Werkzeuge schärfen. Wir müssen auch ein effizientes Produktivitätswerkzeug finden. Kennen Sie sich mit IOC-Containern aus?
Wenn wir in der Vergangenheit eine Methode eines anderen Objekts in einem Objekt aufrufen wollten, haben wir das Objekt manuell durch Neu oder Reflektion erstellt, aber dies jedes Mal zu tun war zu ermüdend und die Kopplung zwischen Klassen Auch sehr hoch.
Über den IOC-Container können wir alle Objekte zur Verwaltung an den Container übergeben. Wir müssen das Objekt nur vor der Verwendung definieren, und wenn das Objekt dann verwendet wird, hilft uns der IOC-Container bei der Initialisierung Objekt, ist das nicht bequemer?
Nachdem Da Yi zu Ende gesprochen hatte, gab er ein Beispiel:
@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(); } }
Der IOC-Container verwendet eine Annotation namens Bean, um beim Systemstart alle von Bean annotierten Klassen zu scannen und diese Klassen zu instanziieren , und speichern Sie dann alle Objekte im Container. Scannen Sie dann alle von Autowired markierten Eigenschaften oder Methoden, finden Sie passende Objekte (nach Name oder Typ usw.) im Container und weisen Sie diesen Eigenschaften bestimmte Objekte zu. Auf diese Weise können wir diese Objekte direkt nutzen. Ist es nicht sehr glücklich, Hand in Hand zu sein?
Lao Yi arbeitet seit fünf Jahren für mich. Nachdem er Da Yis Worten zugehört hatte, stellte er eine Frage. Diese Art von IOC-Container kann für neue Projekte verwendet werden, aber für die alten Projekte ist es so Es ist nicht realistisch, IOC für die Transformation zu nutzen.
Lassen Sie mich ein Beispiel geben. In einem alten Legacy-Projekt gibt es einen Kernschnittstellen-Handler:
public interface Handler<REQ, RES> { RES handle(REQ request); }
Die Handler-Schnittstelle hat viele Implementierungsklassen für unterschiedliche Anforderungen Wenn für die Verarbeitung IOC-Container verwendet werden, ist dies offensichtlich nicht geeignet, da wir vor der Verarbeitung nicht wissen, welche Handler-Implementierungsklasse verwendet werden soll.
Dayi dachte darüber nach: Wenn die Handler-Schnittstelle nur wenige feste Implementierungsklassen hat und nur eine für die Verarbeitung verwendet wird, kann dies beispielsweise durch Konfiguration vor dem Start ermittelt werden. Sie können @Conditional verwenden, um das spezifische Objekt zu bestimmen, das gemäß bestimmten Bedingungen geladen werden soll. Es ist jedoch sehr schwierig, den Typ des Handler-Objekts zu bestimmen, wenn es verwendet wird.
Lao Yi sah, dass alle schwiegen, also fuhr er fort.
Um beim Aufrufen von Methoden unterschiedliche Handler zur Bearbeitung unterschiedlicher Anforderungen zu verwenden, müssen zwei Klassen bestimmt werden, eine ist die Anforderungsklasse und die andere ist die Verarbeitungsklasse, und die Anforderungsklasse und die Verarbeitungsklasse müssen identifiziert werden eins nach dem anderen Entsprechen.
Angenommen, unsere Anforderungsklasse ist eine Paketklasse und jede spezifische Anforderungsklasse erbt von dieser Basisklasse.
Es gibt also viele Möglichkeiten, den Typ jedes einzelnen Pakets zu bestimmen. Sie können jedem Paket einen eindeutigen Namen geben, zum Beispiel:
public abstract class Packet { public abstract String name(); }
Sie können jedem Paket auch einen eindeutigen Namen geben Packet gibt ein Flag an, zum Beispiel:
public abstract class Packet { public abstract int symbol(); }
Aber egal auf welche Weise, jede Packet-Implementierungsklasse muss die Methode in der abstrakten Klasse implementieren, um zu „markieren“, um welche Art von Paket es sich handelt.
Nehmen wir den zweiten Weg als Beispiel und gehen davon aus, dass wir zwei spezifische Pakete haben:
public class RegisterPacket extends Packet { // 注册所需要的其他参数 int symbol() { return 1; } } public class LoginPacket extends Packet { // 登录所需要的其他参数 int symbol() { return 2; } }
Auf diese Weise erfahren wir die Anfrage, wenn wir das Anfrageobjekt erhalten, indem wir request.symbol aufrufen () Um welchen Pakettyp handelt es sich? Zu diesem Zeitpunkt müssen Sie nur die spezifische Handler-Implementierungsklasse finden, um es zu verarbeiten.
Die Anforderungsklasse wurde ermittelt. Wie wird die Handler-Verarbeitungsklasse ermittelt? Können wir auch eine Symbolmethode in der Handler-Schnittstelle definieren, etwa so:
public interface Handler<REQ, RES> { int symbol(); RES handle(REQ request); }
In diesem Fall müssen wir nur die Symbolmethode in allen Implementierungsklassen implementieren, um zu markieren, welche Art von Anfrage der Handler verarbeiten soll . Dürfen.
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){ // 具体的处理方法 } }
Instanziieren Sie abschließend alle Handler-Implementierungsklassen und speichern Sie sie in einem HandlerProvider. Wenn Sie sie verwenden möchten, können Sie sie im HandlerProvider abrufen:
public interface HandlerProvider { Handler getHandler(int symbol); }
So erhalten Sie dann alle Handler als Für die Implementierungsklasse gibt es zwei Methoden.
一种是通过 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){ // 具体的处理方法 } }
这样就将 Handler 的实现和标注进行了解耦了,也可以通过扫描 @Symbol 注解来获取到所有的 Handler 实现类,不过这样做的缺点就是假如我忘记对某个 Handler 实现类添加 @Symbol 注解,到时候就获取不到该 Handler 了。
大家听完老弈的话之后,都陷入了沉思,我靠,还可以这么玩,真有趣。
这时候现在的我,也就是逅弈,说了一句,如果我有一个接口,他只有几个固定的实现类,我不想搞那一套那么重的实现方式,但是我也需要动态的获取实现类来对请求进行处理,那我该怎么办呢?
比如我有一个序列化的接口,如下所示:
public interface Serializer { byte[] serialize(Packet packet); }
然后只有五种具体的序列化的实现类,如下所示:
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) { // 具体的序列化操作 } }
那么我们该怎么确定使用哪种序列化方式对参数 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(); }
然后定义一个 SerializerChooser 根据不同的算法选择不同的 Serializer 实现类即可:
public interface SerializerChooser { Serializer choose(byte algorithm); }
因为根据算法是可以知道对应的序列化接口的,所以就没有必要去扫描了,直接把几种序列化的实现类枚举出来即可,对象的实例可以使用单例模式,如下所示:
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; } } } }
我说完后,大家又一次陷入了沉思,我知道大家都在思考,他们会在每一次思考中获得进步和成长,正如我在思考后得到成长一样。
小鸟总有一天会成长为老鸟,我还走在成长的路上。
Das obige ist der detaillierte Inhalt vonSo verwalten Sie Ihre Objekte in Java besser. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!