装饰器模式和观察者模式

7 篇文章 0 订阅
5 篇文章 1 订阅

装饰器模式

装饰者模式的应用场景

装饰者模式(Decorator Pattern)是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。
装饰者在代码程序中适用于以下场景:

1、用于扩展一个类的功能或给一个类添加附加职责。
2、动态的给一个对象添加功能,这些功能可以再动态的撤销。

例如登录功能
定义操作接口:

public interface IMember {
    public void login(String name,String password);

    public void signUp(String name,String password);
}

默认登录类

public class DefaultMember implements IMember {
    {
        System.out.println("一些初始化动作,获取数据库操作对象等等");
    }
    @Override
    public void login(String name, String password) {
        System.out.println("执行登录逻辑");
    }
    @Override
    public void signUp(String name, String password) {
        System.out.println("执行注册逻辑");
    }
}

QQ登录扩展

public class QQMember implements IMember {
    private IMember member;
    private QQMember() {
        
    }
    public QQMember(IMember member) {
        this.member = member;
    }
    public void loginQQ(String auth) {
        member.login("authPart", "authPart");
    }
    public void signInQQ(String auth) {
        member.signUp("authPart", "authPart");
    }
    @Override
    public void login(String name, String password) {
        member.login(name, password);
    }
    @Override
    public void signUp(String name, String password) {
        member.signUp(name, password);
    }
}

test

    public static void main(String[] args) {
        QQMember qqMember = new QQMember(new DefaultMember());
        qqMember.loginQQ("auth");
    }

原本只支持账号密码登录,扩展后,可以对QQ认证的信息进行处理,再作为账号密码调用原本的登录注册功能.扩展了功能的同时不改变原有对象

装饰者模式在源码中的应用

再例如原型模式中使用到的FileInputStream和ObjectInputStream

    public FileInputStream(String name) throws FileNotFoundException {
        this(name != null ? new File(name) : null);
    }
    public ObjectInputStream(InputStream in) throws IOException {
        verifySubclass();
        bin = new BlockDataInputStream(in);
        handles = new HandleTable(10);
        vlist = new ValidationList();
        serialFilter = ObjectInputFilter.Config.getSerialFilter();
        enableOverride = false;
        readStreamHeader();
        bin.setBlockDataMode(true);
    }
    public static void main(String[] args) throws Exception {
        QQMember qqMember = new QQMember(new DefaultMember());
        qqMember.loginQQ("auth");
        FileOutputStream fileOutputStream = new FileOutputStream("test");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(qqMember);
        FileInputStream fileInputStream = new FileInputStream("test");
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        QQMember o = ((QQMember) objectInputStream.readObject());
        System.out.println(o==qqMember);
    }

通过ObjectInputStream扩展了FileInputStream,使得可以直接将文件流转换为对象.

装饰者模式的优缺点

优点:

1、装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象 扩展功能,即插即用。
3、装饰者完全遵守开闭原则。

缺点:

1、会出现更多的代码,更多的类,增加程序复杂性。
2、动态装饰时,多层装饰时会更复杂

观察者模式

观察者模式的应用场景

观察者模式(Observer Pattern)定义了对象之间的一对多依赖,让多个观察者对象同 时监听一个主体对象,当主体对象发生变化时,它的所有依赖者(观察者)都会收到通 知并更新,属于行为型模式。观察者模式有时也叫做发布订阅模式。观察者模式主要用 于在关联行为之间建立一套触发机制的场景。
观察者模式在现实生活应用也非常广泛, 比如关注用户后的更新通知.

下面用代码实现一个关注后用户更新时,通知所有关注者的观察者模式

Event

public class Event {
    int userId;
    String type;
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Event event = (Event) o;
        return Objects.equals(userId, event.userId);
    }
    @Override
    public int hashCode() {
        return Objects.hash(userId);
    }

    public Event(int userId) {
        this.userId = userId;
    }
    public Event setType(String type) {
        this.type = type;
        return this;
    }
}

Listener

public class Listener {
    int targetId;
    int userId;
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Listener listener = (Listener) o;
        return Objects.equals(userId, listener.userId);
    }
    @Override
    public int hashCode() {
        return Objects.hash(userId);
    }
    public Listener(int targetId, int userId) {
        this.targetId = targetId;
        this.userId = userId;
    }
    public void notifies(Event event) {
        System.out.println("通知" + userId + "," + event.userId + "更新了" + event.type);
    }
}

Publisher

//发布中心
public class Publisher {
    //<用户,监听者集合>
    private Map<Event, LinkedHashSet<Listener>> listeners = new HashMap<>();
    public void publish(Event event) {
        for (Listener listener : listeners.get(event)) {
            listener.notifies(event);
        }
    }
    public void follow(Listener listener) {
        Event event = new Event(listener.targetId);
        if (this.listeners.get(event) != null) {
            this.listeners.get(event).add(listener);
        } else {
            LinkedHashSet<Listener> objects = new LinkedHashSet<>();
            objects.add(listener);
            listeners.put(event, objects);
        }
    }
}

Test

    static Publisher publisher = new Publisher();
    public static void main(String[] args) {
        int userId, userId1, targetId, targetId1;
        userId = 11;
        userId1 = 22;
        targetId = 1;
        targetId1 = 2;
        publisher.follow(new Listener(targetId, userId));
        publisher.follow(new Listener(targetId, userId1));
        publisher.follow(new Listener(targetId1, userId1));
        publisher.follow(new Listener(targetId1, userId));
        update(new Event(targetId).setType("朋友圈"));
        update(new Event(targetId1).setType("心情"));
    }
    public static void update(Event event) {
        publisher.publish(event);
    }

效果

通知11,1更新了朋友圈
通知22,1更新了朋友圈
通知22,2更新了心情
通知11,2更新了心情

观察者模式在源码中的应用

在spring的生命周期中,各个阶段都存在事件和对应的监听(处理)器

org.springframework.boot.context.config.ConfigFileApplicationListener#onApplicationEvent
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ApplicationEnvironmentPreparedEvent) {
			onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
		}
		if (event instanceof ApplicationPreparedEvent) {
			onApplicationPreparedEvent(event);
		}
	}
org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		Executor executor = getTaskExecutor();
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				invokeListener(listener, event);
			}
		}
	}

测试代码

    public static void main(String[] args) {
        SpringApplicationBuilder builder = new SpringApplicationBuilder(PatternTemplateAdapterApplication.class);
        builder.bannerMode(Banner.Mode.OFF);
        builder.listeners(event -> { // 增加监听器
            System.err.println("监听到事件 : " + event.getClass().getSimpleName());
        })
                .run(args)
                .close();
    }

效果

监听到事件 : ApplicationStartingEvent
监听到事件 : ApplicationEnvironmentPreparedEvent
监听到事件 : ApplicationContextInitializedEvent
2020-11-28 20:42:31.206  INFO 30788 --- [           main] c.e.p.PatternTemplateAdapterApplication  : Starting PatternTemplateAdapterApplication on LAPTOP-D2VN2M00 with PID 30788 (D:\Learn2020\project1\pattern\pattern-template-adapter\target\classes started by 87990 in D:\Learn2020\project1\pattern\pattern-template-adapter)
2020-11-28 20:42:31.206  INFO 30788 --- [           main] c.e.p.PatternTemplateAdapterApplication  : No active profile set, falling back to default profiles: default
监听到事件 : ApplicationPreparedEvent
2020-11-28 20:42:31.986  INFO 30788 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-11-28 20:42:31.996  INFO 30788 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-11-28 20:42:31.996  INFO 30788 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.39]
2020-11-28 20:42:32.086  INFO 30788 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-11-28 20:42:32.086  INFO 30788 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 830 ms
2020-11-28 20:42:32.206  INFO 30788 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
监听到事件 : ContextRefreshedEvent
2020-11-28 20:42:32.326  INFO 30788 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-11-28 20:42:32.326  INFO 30788 --- [           main] c.e.p.PatternTemplateAdapterApplication  : Started PatternTemplateAdapterApplication in 1.453 seconds (JVM running for 2.425)
2020-11-28 20:42:32.326  INFO 30788 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
监听到事件 : ServletWebServerInitializedEvent
监听到事件 : ApplicationStartedEvent
监听到事件 : ApplicationReadyEvent
监听到事件 : ContextClosedEvent
Process finished with exit code 0

总结

1、观察者模式支持广播通信。
2、通过事件使得应用的生命周期更清晰
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值