设计模式-1-常用设计模式组合使用

1、策略+工厂模式

定义策略接口,不同的策略都实现这个接口:

public interface HandleSetStatusService {

	public void handleMessage(WebSocketSession session, ImClient imClient,
							  long sellerId, WsRecvWrapper request);
}
/**
 * 策略实现类,
 * 咨询师发送消息
 */
@Service
@HandleServiceStrategyAspect(ImConstants.SELLER_MSG_SEND)
public class HandleSellerMsgSendServiceImpl implements HandleSetStatusService {

	@Autowired
	private ConsultSessionService consultSessionService;
	
	@Override
	public void handleMessage(WebSocketSession session, ImClient imClient,
							  long sellerId, WsRecvWrapper request) {
        XXX
    }

定义注解,用来区分不同的策略实现

@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface HandleServiceStrategyAspect {

    String value();
}
@Component
public class MessageServiceListener implements ApplicationListener {

	@Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
		Map beans = event.getApplicationContext().getBeansWithAnnotation(HandleServiceStrategyAspect.class);
		HandleServiceStrategyFactory handleServiceStrategyFactory = event.getApplicationContext().getBean(HandleServiceStrategyFactory.class);
		beans.forEach((name, bean) -> {
			HandleServiceStrategyAspect typeHandler = bean.getClass().getAnnotation(HandleServiceStrategyAspect.class);
			handleServiceStrategyFactory.register(typeHandler.value(), (HandleSetStatusService) bean);
		});
	}
}

,上边的方法可以在启动时将各个策略注册到下边的工厂当中,定义工厂供使用时获取:

@Service
public class HandleServiceStrategyFactory  {

	private Logger logger = LoggerFactory.getLogger(HandleServiceStrategyFactory.class);

	private static Map services = new ConcurrentHashMap();
	public static HandleSetStatusService getHandleByCommand(String command){
		return services.get(command);
	}
	public static void register(String command,HandleSetStatusService handleSetStatusService){
		Assert.notNull(command,"消息类型不能为空");
		services.put(command,handleSetStatusService);
	}
}

调用:通过前端传入不同的command可以得到不同的策略

@Override
    public void handleTextMessage(long sellerId, WsRecvWrapper request) {

        ImClient imClient = imClientManager.getClientById(String.valueOf(sellerId));
        if (imClient != null) {
            String command = request.getCommand();

            HandleSetStatusService handleSetStatusService = HandleServiceStrategyFactory.getHandleByCommand(command);

            Stopwatch stopWatch = Stopwatch.createStarted();

            handleSetStatusService.handleMessage(wsSessionMap.get(sellerId), imClient, sellerId, request);

            logger.info("ImHandleMessage time: {}", stopWatch.elapsed(TimeUnit.SECONDS));
        }else{
            logger.error("ImHandleMessage error:imClient is null sellerId:"+sellerId);
        }

    }

2、模板模式+观察者模式

模板方法:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤

对应文章介绍:https://www.jianshu.com/p/6463945c94e2 (当中包含钩子方法,钩子方法介绍:https://www.cnblogs.com/yanlong300/p/8446261.html)

观察者模式:https://www.cnblogs.com/adamjwh/p/10913660.html

匿名内部类:

匿名内部类通过扩展某个类或实现某个接口(只能选一种,不能同时发生)来定义,其一般格式为:
new 超类名或接口名(参数列表){
类定义
}
说明:
(1)根据格式可知,匿名内部类一定是在new后面,要么继承一个类,要么实现一个接口,没有类名。
(2)匿名内部类是唯一一个没有构造方法的类。系统会自动调用超类的构造方法。
匿名内部类将实现类,重写方法和创建对象一步合成,从而简化编程的代码量。


java8对匿名内部类的优化:

lambda表达式用得最多的场合就是替代匿名内部类,而实现Runnable接口是匿名内部类的经典例子。lambda表达式的功能相当强大,用()->就可以代替整个匿名内部类。
请看代码:

@Test
public void oldRunable() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("The old runable now is using!");
        }
    }).start();
}

lambda方式:

@Test
public void runable() {
    new Thread(() -> System.out.println("It's a lambda function!")).start();
}

注:这个替代匿名内部类的方式并不是所有情况都适合,Lambda表达式只支持函数式接口。也就是只有一个抽象方法的接口

1、适配器模式:

Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。

Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。

Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
 

预实现,先做一个抽象类,将接口中那些不常用的方法先实现了(空实现),传参的时候传适配器就可以。适用于需要添加的匿名内部类很多。

public class AdaptDemo {
    public static void main(String[] args){
        Button b = new Button();
        //匿名内部类对象,如果不用适配器,则需要重写接口中的所有方法
        b.setListener(new MouseAdapter(){//传入的是适配器,因为它实现了接口
            public void onclick(){            //重写方法
                System.out.println("单击了一下");
            }
        });
        b.click();
        b.dbclick();
        //方法链编程
        new Button().setListener(new MouseAdapter(){
            public void onclick(){
                System.out.println("单击了匿名Button");
            }
            public void onDbclick(){
                System.out.println("双击了匿名Button");
            }
        }).click().dbclick();
    }

}
class Button{
    private MouseListener listener;
    public Button setListener(MouseListener listener){
        this.listener=listener;
        return this;
    }
    public Button click(){
        listener.onclick();
        return this;
    }
    public void dbclick(){
        listener.onDbclick();
    }
}
interface MouseListener{             //鼠标监听器
    public void onclick();
    public void onDbclick();
    public void onRightclick();
    public void onMiddleclick();
}
//适配器,实现接口中的方法,一般是不常用的方法,也可以全部实现,可以空实现,也可以具体实现
abstract class MouseAdapter implements MouseListener{
    public void onclick(){
        System.out.println("单击");
    }
    public void onDbclick(){}
    public void onRightclick(){}
    public void onMiddleclick(){}
}

运行结果:
       单击了一下
       单击了匿名Button
       双击了匿名Button

适配模式和代理模式的区别:

适配器模式是因为新旧接口不一致导致出现了客户端无法得到满足的问题,但是,由于旧的接口是不能被完全重构掉的,因为我们还想使用实现了这个接口的一些服务。那么为了使用以前实现旧接口的服务,我们就应该把新的接口转换成旧接口

代理就不一样了,虽然代理也同样是增加了一层,但是,代理提供的接口和原本的接口是一样的,代理模式的作用是不把实现直接暴露给client,而是通过代理这个层,代理能够做一些处理

你可能感兴趣的:(设计模式)