Detailed explanation of IOC inversion of control examples
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(); } }
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 } }
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(); }
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(); }
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()); } }
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"); }
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."));
上述用例,简要说明了为什么观察者模式是实现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(); }
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!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics



In the Java framework, the difference between design patterns and architectural patterns is that design patterns define abstract solutions to common problems in software design, focusing on the interaction between classes and objects, such as factory patterns. Architectural patterns define the relationship between system structures and modules, focusing on the organization and interaction of system components, such as layered architecture.

The decorator pattern is a structural design pattern that allows dynamic addition of object functionality without modifying the original class. It is implemented through the collaboration of abstract components, concrete components, abstract decorators and concrete decorators, and can flexibly expand class functions to meet changing needs. In this example, milk and mocha decorators are added to Espresso for a total price of $2.29, demonstrating the power of the decorator pattern in dynamically modifying the behavior of objects.

1. Factory pattern: Separate object creation and business logic, and create objects of specified types through factory classes. 2. Observer pattern: allows subject objects to notify observer objects of their state changes, achieving loose coupling and observer pattern.

Design patterns solve code maintenance challenges by providing reusable and extensible solutions: Observer Pattern: Allows objects to subscribe to events and receive notifications when they occur. Factory Pattern: Provides a centralized way to create objects without relying on concrete classes. Singleton pattern: ensures that a class has only one instance, which is used to create globally accessible objects.

The Adapter pattern is a structural design pattern that allows incompatible objects to work together. It converts one interface into another so that the objects can interact smoothly. The object adapter implements the adapter pattern by creating an adapter object containing the adapted object and implementing the target interface. In a practical case, through the adapter mode, the client (such as MediaPlayer) can play advanced format media (such as VLC), although it itself only supports ordinary media formats (such as MP3).

TDD is used to write high-quality PHP code. The steps include: writing test cases, describing the expected functionality and making them fail. Write code so that only the test cases pass without excessive optimization or detailed design. After the test cases pass, optimize and refactor the code to improve readability, maintainability, and scalability.

The Guice framework applies a number of design patterns, including: Singleton pattern: ensuring that a class has only one instance through the @Singleton annotation. Factory method pattern: Create a factory method through the @Provides annotation and obtain the object instance during dependency injection. Strategy mode: Encapsulate the algorithm into different strategy classes and specify the specific strategy through the @Named annotation.

The advantages of using design patterns in Java frameworks include: enhanced code readability, maintainability, and scalability. Disadvantages include complexity, performance overhead, and steep learning curve due to overuse. Practical case: Proxy mode is used to lazy load objects. Use design patterns wisely to take advantage of their advantages and minimize their disadvantages.
