Home > Java > javaTutorial > body text

Detailed explanation of IOC inversion of control examples

零下一度
Release: 2017-07-21 14:33:33
Original
2195 people have browsed it

Control reversal (IOC) is an important feature of the framework, not a special term for object -oriented programming. It has nothing to do with Dependency Injection (DI) and Dependency Lookup.

Using Spring Developers should have some understanding of the IOC control and reversal function. When you first study, you should know that you use dependency injection to achieve the function of IOC. Several design patterns.

Dependency injection to implement IOC

Dependency injection is the most basic implementation method of IOC and the most commonly used object-oriented design One of the ways. How to inject dependencies to achieve the inversion of control effect, start with an example:

public interface UserQueue {void add(User user);void remove(User user);

    User get();

}public abstract class AbstractUserQueue implements UserQueue {protected LinkedList<User> queue = new LinkedList<>();

    @Overridepublic void add(User user) {
        queue.addFirst(user);
    }

    @Overridepublic void remove(User user) {
        queue.remove(user);
    }

    @Overridepublic abstract User get();

}public class UserFifoQueue extends AbstractUserQueue {public User get() {return queue.getLast();
    }

}public class UserLifoQueue extends AbstractUserQueue {public User get() {return queue.getFirst();
    }

}
Copy after login

UserQueue The interface defines public methods for storing in a queue User object. AbstractUserQueue provides some public method implementations for subsequent inherited classes. The last UserFifoQueue and UserLifoQueue implement FIFO and LIFO queues respectively.

This is an effective way to achieve subclass polymorphism.

By creating a client class that depends on the UserQueue abstract type (also known as a service in DI terminology), different implementations can be injected at runtime without having to refactor the code that uses the client class:

public class UserProcessor {private UserQueue userQueue;public UserProcessor(UserQueue userQueue) {this.userQueue = userQueue;
    }public void process() {// process queued users here    }

}
Copy after login

UserProcessor shows that dependency injection is indeed a way of IOC.

We can obtain the dependency on the queue in UserProcessor through some hard-coded methods such as new operation and instantiation directly in the constructor. But this is typical code hard programming, which introduces strong coupling between client classes and their dependencies and greatly reduces testability.

This class declares its dependence on the abstract class UserQueue in the constructor. That is, dependencies are no longer operated by using new in the constructor, but instead are injected externally, either using a dependency injection framework or using the factory or builders pattern.

Using dependency injection, the control of client class dependencies is no longer located in these classes; instead, it is done in the injector, see the following code:

public static void main(String[] args) {
     UserFifoQueue fifoQueue = new UserFifoQueue();
     fifoQueue.add(new User("user1"));
     fifoQueue.add(new User("user2"));
     fifoQueue.add(new User("user3"));
     UserProcessor userProcessor = new UserProcessor(fifoQueue);
     userProcessor.process();
}
Copy after login

The above method achieves the expected effect, and the injection of UserLifoQueue is also simple and clear.

Observer pattern to implement IOC

It is also a common and intuitive way to implement IOC directly through the observer pattern. Broadly speaking, IOC is implemented through observers. The observer pattern is usually used to track the state changes of model objects in the context of model views.

In a typical implementation, one or more observers are bound to an observable object (also called a subject in pattern terminology), for example by calling the addObserver method. Once the binding between the observed and the observer is defined, changes in the state of the observed will trigger the operation of calling the observer. Look at the following example:

public interface SubjectObserver {void update();

}
Copy after login

When the value changes, the call to the above-mentioned very simple observer will be triggered. In real situations, APIs with richer functions are usually provided, such as changing instances that need to be saved, or old and new values, but these do not require observing the action (behavior) mode, so the examples here are as simple as possible.

Below, an observer class is given:

public class User {private String name;private List<SubjectObserver> observers = new ArrayList<>();public User(String name) {this.name = name;
    }public void setName(String name) {this.name = name;
        notifyObservers();
    }public String getName() {return name;
    }public void addObserver(SubjectObserver observer) {
        observers.add(observer);
    }public void deleteObserver(SubjectObserver observer) {
        observers.remove(observer);
    }private void notifyObservers(){
        observers.stream().forEach(observer -> observer.update());
    }

}
Copy after login

In the User class, when its status is changed through the setter method, it will trigger the call bound to it. observer.

Using topic observers and topics, the following examples give the observation method:

public static void main(String[] args) {
    User user = new User("John");
    user.addObserver(() -> System.out.println("Observable subject " + user + " has changed its state."));
    user.setName("Jack");
}
Copy after login

Whenever the state of the User object is modified through the setter method, the observer will be notified and a message printed to the console. So far, a simple use case of the Observer pattern has been given. However, through this seemingly simple use case, we understand how the inversion of control can be achieved in this situation.

In observer mode, the theme plays the role of "framework layer", which completely controls when and where to trigger whose call. The initiative of observers is delegated because observers cannot control when they are called (as long as they are registered with a topic). This means that we can actually find where the inversion of control happened - when the observer is bound to the subject:

user.addObserver(() -> System.out.println("Observable subject " + user + " has changed its state."));
Copy after login

上述用例,简要说明了为什么观察者模式是实现IoC的一种非常简单的方式。正是以这种分散式设计软件组件的形式,使得控制得以发生反转。

 

模板方法模式实现IOC

模板方法模式实现的思想是在一个基类中通过几个抽象方法来定义一个通用的算法,然后让子类提供具体的实现,这样保证算法结构不变。

我们可以应用这个思想,定义一个通用的算法来处理领域实体,看例子:

public abstract class EntityProcessor {public final void processEntity() {
        getEntityData();
        createEntity();
        validateEntity();
        persistEntity();
    }protected abstract void getEntityData();protected abstract void createEntity();protected abstract void validateEntity();protected abstract void persistEntity();

}
Copy after login

processEntity() 方法是个模板方法,它定义了处理实体的算法,而抽象方法代表了算法的步骤,它们必须在子类中实现。通过多次继承 EntityProcessor 并实现不同的抽象方法,可以实现若干算法版本。

虽然这说清楚了模板方法模式背后的动机,但人们可能想知道为什么这是 IOC 的模式。

典型的继承中,子类调用基类中定义的方法。而这种模式下,相对真实的情况是:子类实现的方法(算法步骤)被基类的模板方法调用。因此,控制实际是在基类中进行的,而不是在子类中。

总结:

依赖注入:从客户端获得依赖关系的控制不再存在于这些类中。它存由底层的注入器 / DI 框架来处理。

观察者模式:当主体发生变化时,控制从观察者传递到主体。

模板方法模式:控制发生在定义模板方法的基类中,而不是实现算法步骤的子类中。

The above is the detailed content of Detailed explanation of IOC inversion of control examples. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:php.cn
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
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template