设计模式-观察者模式2

EventBus使用演示及实践案例 本文还有配套的精品资源,点击获取 简介:EventBus 是一个高效的 Android 事件分发库,用于简化组件间通信。本篇将展示如何通过演示项目来理解EventBus的核心概念、安装配置、创建事件类、订阅事件、以及注册和注销订阅者等关键步骤,并演示如何发布事件。通过本Demo,开发者可以掌握EventBus在实际Android开发中的应用。 1. EventBu... 阅读详情

设计模式-观察者模式2

参考链接:https://www.jianshu.com/p/54da3cb6ed8b
上一节我们讲到观察者模式的基本定义、优缺点、适用场景
从父类class通过getDeclaredMethod获取的Method可以调用子类的对象,而子类改写了这个方法,从子类class通过getDeclaredMethod也能获取到Method,这时去调用父类的对象也会报错。虽然这是很符合多态的现象,也符合java的动态绑定规范
本篇内容主要聊一下基于观察者模式的扩展
EVENTBUS
同步阻塞是最经典的实现方式,代码解耦;
异步非阻塞除了能实现代码解耦之外,还能提高代码的执行效率;
进程间的观察者模式解耦更加彻底,一般是基于消息队列来实现,用来实现不同进程间的被观察者和观察者之间的交互。
引出问题:上一节只是说了观察者模式在进程内同步阻塞的情况,接下来聊一下观察者模式在进程异步非阻塞的情况

1、简易实现:

1.1、开线程:

  • 频繁地创建和销毁线程比较耗时,并且并发线程数无法控制,
  • 创建过多的线程会导致堆栈溢出
服务类:
public class PromotionService {
    public void issueNewUserExperienceCash(Long userId){
        System.err.println("新用户体验金来了");
    }
}
public class NotificationService {
    public void sendInboxMessage(Long userId,String msg){
        System.err.println("通知观察者在发送欢迎通知");
    }
}
public class UserService {
    public Long register(String telephone, String password) {
        return 101L;
    }
}
观察者:
public interface Observer {
    void handlerSuccess(Long userId);
}
public class RegNotificationObserver implements Observer {
  private NotificationService notificationService=new NotificationService();
  @Override
  public void handlerSuccess(Long userId) {
    new Thread(()->{
      notificationService.sendInboxMessage(userId, "Welcome...");
    });
  }
}
public class RegPromotionObserver implements Observer {
    private PromotionService promotionService=new PromotionService() ; // 依赖注入
    @Override
    public void handlerSuccess(Long userId) {
        new Thread(()->{
            promotionService.issueNewUserExperienceCash(userId);
        });
    }
}
客户端:
public class UserController {
    private UserService userService=new UserService();
    private static List<RegObserver> regObservers = new ArrayList<>();
    static {//反射
        List<Class> classes = ClassUtil.getImplByInterface(RegObserver.class);
        for (Class aClass : classes) {
            try {
                RegObserver regObserver = (RegObserver) aClass.newInstance();
                regObservers.add(regObserver);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    public  Long register(String telephone,String password){
        //参数校验
       Long userId= userService.register(telephone,password);
        for (RegObserver regObserver: regObservers ) {
            regObserver.handleRegSuccess(userId);
        }
        return userId;
    }
}
public class Client {
    public static void main(String[] args) {
        UserController userController = new UserController();
        System.out.println(userController.register("123", "123"));
    }
}

1.2、线程池

服务类保持不变

  • 利用了线程池解决了第一种实现方式的问题,
  • 缺点:线程池、异步执行逻辑都耦合在了 register() 函数中,难以复用,增加了这部分代码的维护成本。
观察者:
public interface Observer {
    void handlerSuccess(Long userId);
}
public class RegNotificationObserver implements Observer {
  private NotificationService notificationService=new NotificationService();
  @Override
  public void handlerSuccess(Long userId) {
      notificationService.sendInboxMessage(userId, "Welcome...");
  }
}
public class RegPromotionObserver implements Observer {
    private PromotionService promotionService=new PromotionService() ; // 依赖注入
    @Override
    public void handlerSuccess(Long userId) {
            promotionService.issueNewUserExperienceCash(userId);
    }
}
客户端:
public class UserController2 {
    private UserService userService=new UserService();
    private Executor executor= Executors.newFixedThreadPool(20);
    private static List<RegObserver> regObservers = new ArrayList<>();
    static {
        List<Class> classes = ClassUtil.getImplByInterface(RegObserver.class);
        for (Class aClass : classes) {
            try {
                RegObserver regObserver = (RegObserver) aClass.newInstance();
                regObservers.add(regObserver);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
    public  Long register(String telephone,String password){
        //参数校验
       Long userId= userService.register(telephone,password);
        for (RegObserver regObserver: regObservers ) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    regObserver.handleRegSuccess(userId);
                }
            });
        }
        return userId;
    }
}
public class Client {
    public static void main(String[] args) {
        UserController userController = new UserController();
        System.out.println(userController.register("123", "123"));
    }
}

2、EventBus—Google Guava EventBus

事件总线
实现观察者模式的骨架代码
可以基于此框架,非常容易地在自己的业务场景中实现观察者模式
Google Guava EventBus 就是一个比较著名的 EventBus 框架,
它不仅仅支持异步非阻塞模式,同时也支持同步阻塞模式
EventBus eventBus = new EventBus(); // 同步阻塞模式
EventBus eventBus = new AsyncEventBus(Executors.newFixedThreadPool(8));// 异步非阻塞模式

2.1、Guava EventBus的基本方法;

  • register(Object o):注册观察者
    • 这个方法参数为Object,可任意
    • 经典的观察者模式:register() 函数必须接受实现了同一 Observer 接口的类对象。
  • unregister(Object o):解绑观察者
  • post() 函数,用来给观察者发送消息
    • post() 函数发送消息的时候,并非把消息发送给所有的观察者,而是发送给可匹配的观察者。
    • 可匹配:能接收的消息类型是发送消息(post 函数定义中的 event)类型的父类或自己。
  • @Subscribe :标明类中哪个函数可以接收被观察者发送的消息。

2.2、改写上述例子:

服务类不变

导入依赖:
   <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>31.1-jre</version>
        </dependency>
观察者:

不需要编写Observer 接口

public class RegPromotionObserver {
  private PromotionService promotionService; // 依赖注入
  @Subscribe
  public void handleRegSuccess(Long userId) {
    promotionService.issueNewUserExperienceCash(userId);
  }
}
public class RegNotificationObserver {
  private NotificationService notificationService;
  @Subscribe//关键,监听方法,接收被观察者发送的消息
  public void handleRegSuccess(Long userId) {
    notificationService.sendInboxMessage(userId, "...");
  }
}
客户端:
public class UserController {
  private UserService userService; // 依赖注入
  private EventBus eventBus;
  private static final int DEFAULT_EVENTBUS_THREAD_POOL_SIZE = 20;
  public UserController() {
    //eventBus = new EventBus(); // 同步阻塞模式
    eventBus = new AsyncEventBus(Executors.newFixedThreadPool(DEFAULT_EVENTBUS_THREAD_POOL_SIZE)); // 异步非阻塞模式
  }
  public void setRegObservers(List<Object> observers) {
    for (Object observer : observers) {
      eventBus.register(observer);
    }
  }
  public Long register(String telephone, String password) {
    //省略输入参数的校验代码
    //省略userService.register()异常的try-catch代码
    long userId = userService.register(telephone, password);
    eventBus.post(userId);
    return userId;
  }
}
public class Client {
    public static void main(String[] args) {
        UserController userController = new UserController();
        System.out.println(userController.register("123", "123"));
    }
}

2.2、手写EventBus 框架

简介:

在这里插入图片描述
在这里插入图片描述

  • Observer 注册表:关键数据结构,记录了消息类型和可接收消息函数的对应关系
    1. register观察者时,EventBus 通过解析 @Subscribe 注解,生成 Observer 注册表
    2. 调用 post() 函数发送消息的时候,EventBus 通过注册表找到相应的可接收消息的函数
    3. 通过 Java 的反射语法来动态地创建对象、执行函数
  • 同步阻塞模式,EventBus 在一个线程内依次执行相应的函数。
  • 异步非阻塞模式,EventBus 在一个线程池来执行相应的函数。
  • 包括:EventBus、AsyncEventBus、Subscribe、ObserverAction、ObserverRegistry
Subscribe:
  • 一个注解,用于标明观察者中的哪个函数可以接收消息
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
//@Beta
public @interface Subscribe {
}
ObserverAction:
  • 表示 @Subscribe 注解的方法,
  • 其中,target 表示观察者类,method 表示方法。
  • 主要用在 ObserverRegistry 观察者注册表中。
/**
 * 用来表示 @Subscribe 注解的方法,其中,target 表示观察者类,method 表示方法。它主要用在 ObserverRegistry 观察者注册表中。
 */
public class ObserverAction {
    private Object target;
    private Method method;
    boolean override;
    private Class<?> clazz;
    private int                 modifiers;
    public ObserverAction(Object target, Method method) {
        this.target = target;
        this.method = method;
        //设置override为true,默认false
        this.method.setAccessible(true);
    }
    /**
     * 反射执行方法
     * @param event
     */
    public void execute(Object event) { // event是method方法的参数
        try {
//            反射执行方法
            method.invoke(target, event);
        } catch (InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
ObserverRegistry:
  • Observer 注册表,是最复杂的一个类,框架中几乎所有的核心逻辑都在这个类中。这个类大量使用了 Java 的反射语法,不
  • 技巧的: CopyOnWriteArraySet 的使用。
  • CopyOnWriteArraySet,顾名思义,在写入数据的时候,会创建一个新的 set,并且将原始数据 clone 到新的 set 中,在新的 set 中写入数据完成之后,再用新的 set 替换老的 set。这样就能保证在写入数据的时候,不影响数据的读取操作,以此来解决读写并发问题。
  • 除此之外,CopyOnWriteSet 还通过加锁的方式,避免了并发写冲突。
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;
/**
 * 注册中心
 */
public class ObserverRegistry {
  /**
   * 注册中心集合
   */
  private ConcurrentMap<Class<?>, CopyOnWriteArraySet<ObserverAction>> registry = new ConcurrentHashMap<>();
  /**
   * 注册方法
   * @param observer 
   */
  public void register(Object observer) {
    //查找所有的ObserverActions
    Map<Class<?>, Collection<ObserverAction>> observerActions = findAllObserverActions(observer);
    //循环遍历ObserverActions,向注册中心添加注册者
    for (Map.Entry<Class<?>, Collection<ObserverAction>> entry : observerActions.entrySet()) {
      Class<?> eventType = entry.getKey();
      Collection<ObserverAction> eventActions = entry.getValue();
      CopyOnWriteArraySet<ObserverAction> registeredEventActions = registry.get(eventType);
      if (registeredEventActions == null) {
        registry.putIfAbsent(eventType, new CopyOnWriteArraySet<>());
        registeredEventActions = registry.get(eventType);
      }
      registeredEventActions.addAll(eventActions);
    }
  }
  public List<ObserverAction> getMatchedObserverActions(Object event) {
    List<ObserverAction> matchedObservers = new ArrayList<>();
    Class<?> postedEventType = event.getClass();
    for (Map.Entry<Class<?>, CopyOnWriteArraySet<ObserverAction>> entry : registry.entrySet()) {
      Class<?> eventType = entry.getKey();
      Collection<ObserverAction> eventActions = entry.getValue();
      if (postedEventType.isAssignableFrom(eventType)) {
        matchedObservers.addAll(eventActions);
      }
    }
    return matchedObservers;
  }
  private Map<Class<?>, Collection<ObserverAction>> findAllObserverActions(Object observer) {
    Map<Class<?>, Collection<ObserverAction>> observerActions = new HashMap<>();
    Class<?> clazz = observer.getClass();
    for (Method method : getAnnotatedMethods(clazz)) {
      Class<?>[] parameterTypes = method.getParameterTypes();
      Class<?> eventType = parameterTypes[0];
      if (!observerActions.containsKey(eventType)) {
        observerActions.put(eventType, new ArrayList<>());
      }
      observerActions.get(eventType).add(new ObserverAction(observer, method));
    }
    return observerActions;
  }
  private List<Method> getAnnotatedMethods(Class<?> clazz) {
    List<Method> annotatedMethods = new ArrayList<>();
    for (Method method : clazz.getDeclaredMethods()) {
      if (method.isAnnotationPresent(Subscribe.class)) {
        Class<?>[] parameterTypes = method.getParameterTypes();
        Preconditions.checkArgument(parameterTypes.length == 1,
                "Method %s has @Subscribe annotation but has %s parameters."
                        + "Subscriber methods must have exactly 1 parameter.",
                method, parameterTypes.length);
        annotatedMethods.add(method);
      }
    }
    return annotatedMethods;
  }
}
EventBus

EventBus 实现的是阻塞同步的观察者模式。看代码你可能会有些疑问,这明明就用到了线程池 Executor 啊。实际上,MoreExecutors.directExecutor() 是 Google Guava 提供的工具类,看似是多线程,实际上是单线程。之所以要这么实现,主要还是为了跟 AsyncEventBus 统一代码逻辑,做到代码复用。

import java.util.List;
import java.util.concurrent.Executor;
public class EventBus {
  private Executor executor;
  private ObserverRegistry registry = new ObserverRegistry();
  public EventBus() {
    this(MoreExecutors.directExecutor());// Google Guava 提供的工具类,看似是多线程,实际上是单线程
  }
  protected EventBus(Executor executor) {
    this.executor = executor;
  }
  public void register(Object object) {
    registry.register(object);
  }
  public void post(Object event) {
    List<ObserverAction> observerActions = registry.getMatchedObserverActions(event);
    for (ObserverAction observerAction : observerActions) {
      executor.execute(new Runnable() {
        @Override
        public void run() {
          observerAction.execute(event);
        }
      });
    }
  }
}
AsyncEventBus
  • 基于 EventBus,
  • 在构造函数中,由调用者注入线程池。
public class AsyncEventBus extends EventBus {
  public AsyncEventBus(Executor executor) {
    super(executor);
  }
}

3、jdk和spring中使用:

3.1、jdk

  1. 定义一个观察者实现了Observer接口
    1. 重写update方法,两个参数(Observerable,args)
  2. 定义一个被观察者继承Observerable类
    1. 在此类中自定义方法,编写逻辑,在其中
    2. super.setChanged()
    3. super.notifyObservers()/super.notifyObservers(args);//唤醒
  3. 客户端编写调用,添加观察者
  • java.util下一个接口(Observer),一个类(Observerable)
  • Observerable: 被观察者
    • Vector 集合成员变量,用于保存所有要通知的观察者对象
    • void addObserver(Observer o) 方法:用于将新的观察者对象添加到集合中。
    • void notifyObservers(Object arg) 方法:调用集合中的所有观察者对象的 update方法,通知它们数据发生改变。通常越晚加入集合的观察者越先得到通知。
    • void setChange() 方法:用来设置一个 boolean 类型的内部标志,注明目标对象发生了变化。它为true时,notifyObservers() 才会通知观察者。
  • Observer 接口
    • 监视目标对象的变化,当目标对象发生变化时,观察者得到通知,并调用 update 方法,进行相应的工作。
  • 代码:
public class Client {
    public static void main(String[] args) {
        Thief thief = new Thief("隔壁老王");
        PoliceMan policeMan = new PoliceMan("智勇双全");
        thief.addObserver(policeMan);
        thief.steal();
    }
}
/**
 * 被观察者
 */
public class Thief extends Observable {
    private String name;
    public Thief(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void steal(){
        System.err.println("小偷:偷东西哦");
//        注明目标对象发生了变化。当它为true时,notifyObservers() 才会通知观察者。
        super.setChanged();
        super.notifyObservers();//唤醒

    }
}
//观察者
public class PoliceMan implements Observer {
    private String name;
    public PoliceMan(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    //args,为notifyObservers里面的参数,来限定观察者
    @Override
    public void update(Observable o, Object arg) {
        System.err.println("警察:别动,小偷xxx"+((Thief)o).getName());
    }
}

3.2、spring中使用:

Spring观察者模式包含三部分:

  • Event 事件(相当于消息)
  • Listener 监听者(相当于观察者)
  • Publisher 发送者(相当于被观察者)
    定义一个继承 ApplicationEvent 的事件(DemoEvent);
    定义一个实现了 ApplicationListener 的监听器(DemoListener);
    定义一个发送者(DemoPublisher),
    发送者调用 ApplicationContext 来发送事件消息。
源码:
public abstract class ApplicationEvent extends EventObject {
	private static final long serialVersionUID = 7099057708183571937L;
	private final long timestamp;
	public ApplicationEvent(Object source) {
		super(source);
		this.timestamp = System.currentTimeMillis();
	}
	public final long getTimestamp() {
		return this.timestamp;
	}
}
public class EventObject implements java.io.Serializable {
    private static final long serialVersionUID = 5516075349620653480L;
    protected transient Object  source;
    public EventObject(Object source) {
        if (source == null)
            throw new IllegalArgumentException("null source");

        this.source = source;
    }
    public Object getSource() {
        return source;
    }
    public String toString() {
        return getClass().getName() + "[source=" + source + "]";
    }
}
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
	void onApplicationEvent(E event);
}
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
  private final Set<ApplicationListener<?>> applicationListeners;
  public AbstractApplicationContext() {
    this.applicationListeners = new LinkedHashSet();
    //...
  }
  public void publishEvent(ApplicationEvent event) {
    this.publishEvent(event, (ResolvableType)null);
  }
  public void publishEvent(Object event) {
    this.publishEvent(event, (ResolvableType)null);
  }
  protected void publishEvent(Object event, ResolvableType eventType) {
    //...
    Object applicationEvent;
    if (event instanceof ApplicationEvent) {
      applicationEvent = (ApplicationEvent)event;
    } else {
      applicationEvent = new PayloadApplicationEvent(this, event);
      if (eventType == null) {
        eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
      }
    }
    if (this.earlyApplicationEvents != null) {
      this.earlyApplicationEvents.add(applicationEvent);
    } else {
      this.getApplicationEventMulticaster().multicastEvent(
            (ApplicationEvent)applicationEvent, eventType);
    }
    if (this.parent != null) {
      if (this.parent instanceof AbstractApplicationContext) {
        ((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
      } else {
        this.parent.publishEvent(event);
      }
    }
  }
  public void addApplicationListener(ApplicationListener<?> listener) {
    Assert.notNull(listener, "ApplicationListener must not be null");
    if (this.applicationEventMulticaster != null) {
    this.applicationEventMulticaster.addApplicationListener(listener);
    } else {
      this.applicationListeners.add(listener);
    }  
  }
  public Collection<ApplicationListener<?>> getApplicationListeners() {
    return this.applicationListeners;
  }
  protected void registerListeners() {
    Iterator var1 = this.getApplicationListeners().iterator();
    while(var1.hasNext()) {
      ApplicationListener<?> listener = (ApplicationListener)var1.next();     this.getApplicationEventMulticaster().addApplicationListener(listener);
    }
    String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false);
    String[] var7 = listenerBeanNames;
    int var3 = listenerBeanNames.length;
    for(int var4 = 0; var4 < var3; ++var4) {
      String listenerBeanName = var7[var4];
      this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }
    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;
    if (earlyEventsToProcess != null) {
      Iterator var9 = earlyEventsToProcess.iterator();
      while(var9.hasNext()) {
        ApplicationEvent earlyEvent = (ApplicationEvent)var9.next();
        this.getApplicationEventMulticaster().multicastEvent(earlyEvent);
      }
    }
  }
}
  • 观察者需要事先注册到被观察者(JDK 的实现方式)或者事件总线(EventBus 的实现方式),spring中观察者注册到了 ApplicationContext 对象
  • ApplicationContext 这个类并不只是为观察者模式服务的。它底层依赖 BeanFactory(IOC 的主要实现类),提供应用启动、运行时的上下文信息,是访问这些信息的最顶层接口。
  • ApplicationContext 只是一个接口,具体的代码实现包含在它的实现类 AbstractApplicationContext 中。这里把跟观察者模式相关的代码
  • 真正的消息发送,实际上是通过 ApplicationEventMulticaster 这个类来完成
    • multicastEvent() :消息发送函数
    • 它通过线程池,支持异步非阻塞、同步阻塞这两种类型的观察者模式。
代码实现:
/**
 * 消息
 */
public class DemoEvent extends ApplicationEvent {

    private String message;
    /**
     * Create a new {@code ApplicationEvent}.
     *
     * @param source the object on which the event initially occurred or with
     *               which the event is associated (never {@code null})
     */
    public DemoEvent(Object source) {
        super(source);
    }

    public String getMessage() {
        return message;
    }
}
/**
 * 观察者
 */
public class DemoListener implements ApplicationListener<DemoEvent> {
    @Override
    public void onApplicationEvent(DemoEvent event) {
        String message = event.getMessage();
        System.err.println(message);
    }
}
// Publisher发送者,被观察者
@Component
public class DemoPublisher {
  @Autowired
  private ApplicationContext applicationContext;
  public void publishEvent(DemoEvent demoEvent) {
    this.applicationContext.publishEvent(demoEvent);
  }
}
Java线程池及观察者模式解决多线程意外死亡重启问题 Java线程池及观察者模式解决多线程意外死亡重启问题,附件含两个要运行代码! 立即下载

相关推荐

Android逆向实战:抖音聊天记录数据库解析与导出(附完整SQL查询)

本文提供了一份Android逆向实战指南,详细解析了抖音聊天记录数据库的结构与导出方法。通过定位核心的`im.db`数据库文件,深度解读conversation_list、msg等关键表结构,并附上完整的SQL查询语句,帮助开发者或研究人员安全、合规地提取和分析本地聊天数据,适用于移动安全研究与数据分析场景。

mango的博客 861

【EventBus】EventBus 源码解析 ( 事件发送 | 线程池中执行订阅方法 )

一、EventBus 中主线程支持类、 二、EventBus 中 AsyncPoster 分析、 三、AsyncPoster 线程池 Runnable 任务类、

让 学习 成为一种 习惯 ( 韩曙亮 の 技术博客 ) 1058

百度新闻评论内容抓取

通过抓包分析发现,百家号手机app端文章评论内容接口为:https://ext.baidu.com/api/comment/v1/comment/getlist?appid=101&sid=1008524_2-1010050_1-1007549_23033-1007550_23035&cuid=01B5EAF73E8A83BB842BE04E4FB6C656|23200201047...

codingforhaifeng的博客 2422

EventBus 线程池 OOM 案例浅析

线程池OOM案例浅析 背景 近来公司压测,发现 EventBus 使用异步线程池时,发现出现大量线程阻塞,导致线程池不可用。 线程池执行流程 ![](https://images2018.cnblogs.com/blog/1012259/201804/1012259-20180425224622974-1285062397.png) 源码分析 guava 中EventBus 异步线程池执行的方式默...

weixin_30838873的博客 458

从零搭建智能车竞赛卡丁车:自动驾驶科目中的锥桶识别与路径规划实战教程

本文提供了一份从零搭建智能车竞赛卡丁车的实战教程,重点解析了自动驾驶科目中的核心环节:锥桶识别与路径规划。内容涵盖硬件选型、传感器部署、基于单目视觉的识别算法,以及适用于嵌入式平台的轻量级路径规划与跟踪策略,旨在帮助参赛者与爱好者系统掌握关键技术,完成稳定可靠的自动驾驶系统搭建。

lg888的博客 1169

SAP ERP 系统订单行项目中的计划行(Scheduled Line)详解

*计划行(Scheduled Line)**是SAP系统中订单行项目(如采购订单、生产订单、销售订单等)的细分单元,用于记录和管理物料的供应或需求计划。每个订单行项目可以包含一个或多个计划行,每个计划行对应具体的交货日期、数量、需求类型(如采购申请、生产订单、库存调拨等)以及相关的物流控制参数。简单来说,计划行是系统对某一物料在特定时间点的供需状态的详细规划,它将订单分解为更细粒度的执行步骤,确保供应链各环节(如采购、生产、仓储)能够按计划协同工作。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 3392

腾讯Android岗三面:EventBus 发送的消息,如何做到线程切换?

一. 前言 EventBus 是一个基于观察者模式的事件订阅/发布框架,利用 EventBus 可以在不同模块之间,实现低耦合的消息通信。 EventBus 因为其使用简单且稳定,被广泛应用在一些生产项目中。 通常我们就是使用 EventBus 分发一些消息给消息的订阅者,除此之外我们还可以通过 EventBus 将消息传递到不同的线程中去执行,处理消息。这其中还涉及到一些线程切换问题、线程池的问题,在使用的过程中,还有一些配置的选择,此时我们需要根据不同的业务场景,来选择不同的线程切换方式。 本文就 E.

Androiddddd的博客 474

基于 ESP32 的智能环境监测与远程报警系统

本系统基于 ESP32 实现了环境数据的采集、远程传输、报警通知功能,硬件成本低、接线简单、代码可直接运行。用户可根据实际需求调整传感器类型(如替换为 PM2.5 传感器、土壤湿度传感器)和功能扩展,适合嵌入式 IoT 入门学习和实际场景应用。通过多轮迭代(如优化低功耗、增加 APP 控制),可进一步提升系统的实用性和稳定性。

weixin_42300449的博客 1025

观察者 模式 和 线程池的应用

1.线程池的使用核心对象解释:ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize, keepAliveTime,TimeUnit.SECONDS,newLinkedBlockingQueue<Runnable>(), Executors.defaultThreadFactory

开心的哈士奇 533

第076篇:大涡模拟进阶

大涡模拟(Large Eddy Simulation, LES)是介于直接数值模拟(DNS)和雷诺平均方法(RANS)之间的重要湍流模拟方法。本文深入介绍动态Smagorinsky模型、尺度相似模型、WALE模型和隐式LES等高级亚格子模型。通过Python实现槽道湍流和圆柱绕流的LES求解器,详细展示近壁面处理、动态系数计算和亚格子应力建模技术。大涡模拟,亚格子模型,动态模型,Smagorinsky,WALE模型,尺度相似模型,近壁面处理,槽道湍流LES的核心思想是对Navier-Stokes方程进行空间

2401_87715305的博客 740

RGB渐变颜色转换公式及例程

设Step=51,将RGB(255,0,0)颜色渐变为RGB(0,255,0),即RGB(255,0,0)经过51次颜色渐变转为RGB(0,255,0);设Step=51,将RGB(0,255,0)颜色渐变为RGB(0,0,255),即RGB(0,255,0)经过51次颜色渐变转为RGB(0,0,255);设Step=51,将RGB(0,0,255)颜色渐变为RGB(255,0,0),即RGB(0,0,255)经过51次颜色渐变转为RGB(255,0,0);分步骤的方式,使其渐变色循环整个颜色值;

1万+

HeadFirst设计模式(2)-观察者模式(ObserverPattern)

第二节观察者模式(OberverPattern) HeadFirst这节给出的案例是气象站监测应用系统。背景是:气象站通过物流装置获取气象信息,生成WeatherData(气象数据),最后通过布告板显示给用户。WeatherData可以获取物理装置的数据,并且将数据显示到布告板上:目前状况(温度、湿度、气压)、气象统计和天气预报。 先给出一个错误示范 public class...

weixin_30481087的博客 97

若依开源系统多数据源整合clickhouse数据库详细步骤

若依开源系统多数据源整合clickhouse数据库详细步骤

java之书-资源 898

【设计模式总结】2、观察者模式

观察者模式是个非常常见的设计模式,窗体程序中、MVC等都大量使用了观察者模式。 观察者模式,可以理解为采用订阅、推送消息的方式实现一对多(其实还可以‘拉’消息,Java内置库中有相应实现,但‘推’更为巧妙,本文实现用的‘推’)。 概念 在对象之间定义一对多依赖,这样一来,当一个对象改变状态,依赖他的对象会收到通知,并自动更新。 主题与观察者 *《HeadFirst 设计模式》*一书中提到了订阅报纸...

Alexander的博客 285

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

设计模式-2、观察者模式

设计模式-2、观察者模式一、观察者模式代码二、代理模式基础铺垫正式项目 一、观察者模式 1、不断的询问 游戏开发场景:     1、播放一个大招,在0.5秒时播放一个音效 代码 using System.Collections; using System.Collections.Generic; using UnityEngine; public class ObserverBase : MonoBehaviour { //此部分为伪代码 Anima

爱吃芒果的薯条 的博客 194

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

设计模式2——观察者模式

1 模式说明 观察者模式就像报纸出版商和订阅者一样,在观察者模式中出版者叫做“主题”(subject),订阅者叫做“观察者”(observer)。主题内的数据产生变化时,就会通知已经订阅该主题的观察者,观察者如果订阅了主题,一旦主题发生变化发来通知,观察者就会做出相应的调整。观察者的数量可以是很多,换句话说就是很多人可以同时订阅一个主题。主题和观察者是1对多的关系。 说明 (1)如图1,一共有5个...

可耳(keer)的博客 213

设计模式2-》观察者模式

观察者模式 当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。 介绍 意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。 何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。 如何解决:使用

TonyWu的博客 524

《HF 设计模式》 C2 观察者模式

文章目录1 模拟气象站布告1.1 一个错误示范1.2 报社和订阅者1.3 定义观察者模式1.4 松耦合1.5 实现气象站布告系统1.6 观察者获取数据的方式1.7 Java内置的观察者模式1.8 设立changed标志1.9 1 模拟气象站布告 在一个模拟气象站布告的应用中,我们将建立3个布告板,分别为:目前天气,气象统计和天气预报,气象站提供了一个WeatherData对象,它能够及时获取温度,湿度,气压等信息,我们根据WeatherData获取到的数据来更新这几个布告板 但是布告板的更新是由用户

weixin_43541094的博客 491

设计模式(2)——观察者模式

观察者(Observer)模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。 观察者模式的原理有点类似报纸和杂志的订阅。 报社的业务就是出版报纸 向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来。只要你是他们的订户,你就会一直收到新报纸。 当你不想再看报纸的时候,取消订阅,他们就不会再送新报纸来。 只要报社还在运营,就会一直有人(...

liinux-Talk is cheap,show me the code. 170

设计模式2-观察者模式

观察者模式,模拟气象站与布告栏

weixin_44200074的博客 193

设计模式:观察者模式(2)

推模式 被观察对象向观察者推送主题的详情信息时候,不管观察者是否需要,这些信息一般是观察目标对象的全部或者部分信息。一般这种模型实现通过update(objectobj)将观察目标对象传入进去。 拉模式 被观察对象通知观察者时候,只发送少量的信息,如果需要详情信息时候,被观察对象也就是观察目标对象主动到观察者当中,然后通过观察目标对象老获取信息。通常使用update(suObject s...

来杯Java 3021

设计模式-2 观察者模式

观察者模式

小飞将的博客 189

观察者模式--设计模式2

observer模式 它的作用是当一个对象的状态发生变化时,能够自动通知其他的关联的对象。 自动刷新对象状态。 观察者模式角色和职责 Subject 被观察者 被观察的对象,当需要被观察的状态发生变化时,需要通知队列中所有的观察者对象。 subject需要维护(添加,删除,通知)一个观察者对象的列表。 Concreate subject 被观察者的具体实现。 Observer 观察者 接口或者抽象...

u010160949的博客 109

设计模式笔记_10_观察者模式2

package com.maom.design.D_10_Observer; import java.util.Observable; import java.util.Observer; /** * @Title: D_10_Observer * @Description: 10_观察者模式 * @Author maohb * @Version 1.0 */ public cla...

七点十七 137

《HeadFirst设计模式》(2)——观察者模式

模式定义 观察者模式(Observer) 定义了对象间的一对多依赖,当一个对象(Subject)状态改变时,所有的依赖者(Observer)都会收到通知并自动更新。 设计原则 松耦合设计。 双方不知道彼此具体是什么,只知道对方是一类的对象就行。 问题场景 有一个气象站,存有采集到的气象数据。目前的工作为建立一个应用,利用气象站的数据更新三个布告板:目前状况、气象统计、天气预报。要求当气象数据更新...

一生悬命的博客 271
上一篇: 程序员需要明白的指标
下一篇: git-reset
ADCQprogrammer
博客等级 码龄7年 4粉丝 21原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值