拥有授权登录用户的应用程序设计

今天实在太忙, 去公司加班回来之后,继续帮朋友实现网站,今晚的任务是设计一套框架以兼容上一篇文章所说的QQ, 新浪微博授权登录和之后的其他操作, 这套框架的设计目标是有有良好的扩展性(以方便以后可以实现更多公司的第三方登录), 和模块独立性(如果第三方提供的API变动,不会太影响自己的网站逻辑)。 

定好需求之后第一个想到就是使用桥梁模式。 以下是第一个设计版本。


抽象类TPAgent 是模块的门户,对外它是主要提供访问接口。 这里我只列出了一个示例方法 createTPAccountBinding(), 这个方法将返回 TPAccountBinding 类实例, 而这个类包含了用户在第三方网站里面的一些信息。 桥梁模式的另一头是接口RemoteResourceProxy. (图有误,实在懒得换了) , 所有TPAgent 的子类都应该持有这个接口,并且要通过这个接口完成所需要的逻辑。 而RemoteResourceProxy 接口的继承类可以使用第三方NATIVE API了,例如SinaweiboReousceProxy 使用Sinaweibo的JAVA API,  QQResourceProxy 类里面使用腾讯的API。 

通过这种设计我相信应该可以的满足扩展性和模块独立性的需求了。 例如,如果我们以后要加入人人网的第三方登录功能,我们就可以实现一个例如RenRenResouceProxy的类并且使它实现RemoteResourceProxy接口, 然后在枚举类TPType里增加新的 一项。  而且由于TPAgent 面向的是RemoteResourceProxy接口,不直接面对各个NATIVE API, 所有就算是我们升级NATIVE API, 或者加入新的RemoteResourceProxy接口实现类, TPAent的子类逻辑也不会跟着做变动, 我们只要调整相应的RemoteResourceProxy接口实现类就可以了。 


看似设计就这么简单的结束了,其实不然, 不多想象的话以上设计就是纸上谈兵。 因为有两个问题没有考虑到。 1 RemoteResourceProxy接口实现类一般都需要一些参数,例如OAUTH 的 这些 ResourceProxy 一定需要为了得到ACCESS TOKEN 的 code。  如何优雅的传入参数是一个问题。  2 我们的TPAgent  有一个静态的Build 方法,我们应该如何根据TPType的值来构建相应的TPAgent 子类。 

对于第一个问题我首先想到的是用ThreadLocal,这样我们可以在的STRUTS Action 中直接用ThreadLocal.set()设置参数,而我们的RemoteResourceProxy接口实现类可以简单的通过ThreadLocal.get() 得到ACTION 的值。 这种想法有点象STRUTs2里面的 ActionContext的使用. 但是用 ThreadLocal 有一些弊端, 例如如果我希望开一个额外的线程来完成微博分享操作,那么ThreadLocal马上就变得失效。  所以最后我还是设计了一个TPAgentBuildingParam 类, 用户在调用TPAgent类的build方法的时候需要传入这个类的实例。 

public class TPAgentBuildingParam 
{
	public static int OAUTH_CODE = 1;
	
	private Map<Integer, Object> paramMap = new HashMap<Integer, Object>();
	
	public void setOAuthCodeForAccessToken(String code){
		paramMap.put(OAUTH_CODE, code);
	}
	
	public String getOAuthCode()
}


对于第二个问题, 我想到BUILDER 模式, 我可以设计的一个Builder 类, 然后让这个类根据传入的TPType, 和TPAgentBuildingParam  创建对应的TPAgent 子类实例。而TPAgent 的静态方法Build()将会使用这个Builder 类。  但是一想到要在这个类里写一大堆IF ELSE. 我就放弃了。因为每次我加新的RemoteResourceProxy接口实现类都会在这个类加新的IF ELSE 条件语句, 这样破坏了开闭原则。   我最后想到的方法是借用TPType 这个枚举类。 因为每次加入新的RemoteResourceProxy接口实现类的时候,我都要在这个枚举类里面添加新的项, 如果我把一些如何构建TPAgent 子类的信息放入这个项里面,那就会变得方便很多。 于是把一开始想的Builder 类设计成一个接口,而对于每个第三方登录的源,我都实现一个对应的Builder接口实现类

interface TPAgentBuilder 
{
	TPAgent build( TPAgentBuildingParam param);
}


class QQresourceAgentBuilder implements TPAgentBuilder 
{
	@Override
	public TPAgent build(TPAgentBuildingParam param) 
	{
		if(param.getParam(TPAgentBuildingParam.OAUTH_CODE)==null)
			throw new IllegalArgumentException("oauth code is empty in TPAgentBuildingParam instance");
		
		QQResourceProxy proxy = new QQResourceProxy(param.getOAuthCode());
		return new OAuthTPAgent(proxy);
	}

}

class SinaweiboResourceAgentBuilder implements TPAgentBuilder 
{
	@Override
	public TPAgent build(TPAgentBuildingParam param) 
	{
		if(param.getParam(TPAgentBuildingParam.OAUTH_CODE)==null)
			throw new IllegalArgumentException("oauth code is empty in TPAgentBuildingParam instance");
		
		QQResourceProxy proxy = new QQResourceProxy(param.getOAuthCode());
		return new OAuthTPAgent(proxy);
	}

}

public enum TPType {
	SINAWEIBO(TPAgentBuildingParam.OAUTH_CODE,SinaweiboResourceAgentBuilder.class),
	QQ(TPAgentBuildingParam.OAUTH_CODE, QQresourceAgentBuilder.class );
	
	int paramField;
	Class<? extends TPAgentBuilder> agentBuilerClass;
	
	private TPType(int i, Class<? extends TPAgentBuilder> agentBuilerClass)
	{
		paramField= i;
		this.agentBuilerClass = agentBuilerClass;
	}
}

public abstract class TPAgent {

	public abstract TPAccountBinding createTPAccountBinding();
	
	
	public static TPAgent build(TPType type, TPAgentBuildingParam param) throws TPAgentException
	{
		try {
			TPAgentBuilder builder = (TPAgentBuilder)type.agentBuilerClass.newInstance();
			return builder.build(param);
		} catch (Exception e) {
			throw new TPAgentException(e);
		}
	}

	
}

这么设计的好处是如果我们添加新的RemoteResourceProxy接口实现类的时候, 我要做的就是 1. 写一个新的Builder实现类,2. 在枚举类添加新的项然后把新的Builder实现类的Class 作为实例传入。 


最后整体的设计变为


设计终于完成了, 不过现在太晚了,脑子不做主了,说不定还有什么遗漏的东西,不管了以后边做边改吧。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值