迪米特法则(Law of Demeter,LoD)也称为最少知识原则(Least Knowledge Principle,LKP)。如果两个类不必彼此间直接通信,那么这两个类不应该发生直接的相互关系。如果其中一个类需要调用另一个类的方法,可以通过第三者转发这个调用。
迪米特法则还有一个英文解释:Only talk to your immedate friends(只与直接的朋友通信)。对于这个翻译,我觉得是很贴切很合理的,引用《Java与模式》中的事例来说,张三和李四是朋友,李四和王五是朋友,张三和王五互不认识,如果张三想调用王五的方法,在迪米特法则的前提下,应该是张三通过李四去调用王五的方法,这就是为什么翻译为只与直接的朋友通信。
一句话总结上述两个解释:只与朋友通信,不要搭理陌生人。
先不说那么多了,直接用Android实际开发中的应用来实现来逐步说明这个法则,MVP是当今开发中一个比较流行的模式,本篇通过MVP模式加上adapter来说明,先上一张图片然后撸代码,图片如下:
这个布局方式有N种方式,根据个人喜好,这个界面之前是别人帮我写的,采用relativeLayout布局,toolbar+recyclerview+button,当时做到这个页面的时候,想到有三套解决方案,如下:
1、实例化adapter的时候,把presenter传过来
2、实例化的时候把activity传过来
3、通过接口,让adapter通过调用activity,在activity中调用presenter
乍一看,可能觉得第三种最烦,前两种方案,只需要在adapter中直接调用就可以,但是就把adapter与activity或者presenter关联到一起了,把activity传过来,这完全是一个很脑残的想法,adapter是activity中的一部分,再把activity传过来,这就形成了一个强耦合。presenter和adapter是完全不相关的两个类,如果把presenter传过来,两个完全没关系的类关联到一起,这样真的好吗?类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
当我们刚学软件开发的时候,书本上就提示“高内聚低耦合”,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。低耦合的优点不言而喻,但是怎么样编程才能做到低耦合呢?那正是迪米特法则要去完成的。
EvaluationListActivity.java
/**
*
* created by zero on 2016-6-27
*
* 评价列表
*
*/
public class EvaluationListActivity extends
BaseActivity<EvaluationListPresenter> implements IEvaluation
{
@ViewInject(R.id.rv_goods_reviews)
private RecyclerView rv_goods_reviews;
@ViewInject(R.id.btn_submit)
private Button btn_submit;
private EvaluationListAdapter adapter;
@Override
protected int initLayout() {
// TODO Auto-generated method stub
return R.layout.activity_goods_reviews;
}
@Override
protected void initViewAndListen() {
// 调用父类,初始化toolbar,在此不暴露父类,(*^__^*) 嘻嘻……
super.initToolbar();
// 调用父类,初始化LinearLayoutManager
rv_goods_reviews.setLayoutManager(super
.initLayoutManager(LinearLayoutManager.VERTICAL));
btn_submit.setOnClickListener(this);
}
@Override
protected void initData() {
// TODO Auto-generated method stub
presenter.initData();
}
@Override
protected void onclick(View v) {
// TODO Auto-generated method stub
switch (v.getId())
{
case R.id.btn_submit:
submit();
break;
default:
break;
}
}
// 通过回调函数,抓取map
private void submit() {
// TODO Auto-generated method stub
adapter.getMap(new IEvaluationMap()
{
@Override
public void sendEvaluationData(Map<String, EditText> map) {
// TODO Auto-generated method stub
List<EvaluationListToJsonModel> list = new ArrayList<EvaluationListToJsonModel>();
for (Map.Entry<String, EditText> mEntry : map.entrySet())
{
EvaluationListToJsonModel model = new EvaluationListToJsonModel();
model.goods_id = mEntry.getKey();
model.comment = mEntry.getValue().getText().toString();
list.add(model);
}
/**
* 服务器那边需要传送base64,base64基于mime协议,每76字符便会回车,所以此处需要去除回车,
* 关于base64有疑问,请看相关资料
*/
String comment = Base64.encodeToString(
GsonHelper.toJson(list).getBytes(), Base64.DEFAULT).replace("\n", "");
if (!TextUtils.isEmpty(comment))
{
presenter.submit(comment);
} else
{
toast("error");
}
}
});
}
@Override
protected Class<EvaluationListPresenter> getPsClass() {
// TODO Auto-generated method stub
return EvaluationListPresenter.class;
}
@Override
protected Object getIView() {
// TODO Auto-generated method stub
return this;
}
@Override
public void sendData(OrderMainModel mModel) {
adapter = new EvaluationListAdapter(EvaluationListActivity.this,
mModel.goods_list);
rv_goods_reviews.setAdapter(adapter);
}
}
EvaluationListAdapter.java
/**
*
* created by zero on 2016-6-27
*
* 评价列表的adapter
*
*/
public class EvaluationListAdapter extends RecyclerView.Adapter<ViewHolder>
{
private Context context;
private List<OrderGoodsModel> list ;
private Map<String, EditText> map = new LinkedHashMap<String, EditText>();
public EvaluationListAdapter(Context context, List<OrderGoodsModel> list) {
this.context = context;
this.list = list;
}
@Override
public int getItemCount() {
// TODO Auto-generated method stub
return list==null?0:list.size()+1;
}
@Override
public void onBindViewHolder(ViewHolder holder, int posiotn) {
// TODO Auto-generated method stub
if(holder instanceof ItemViewHolder){
ImageUtil.display(context, list.get(posiotn).goods_thumb, ((ItemViewHolder) holder).iv_gift_img);
((ItemViewHolder) holder).tv_gift_price.setText("¥"+list.get(posiotn).goods_price);
((ItemViewHolder) holder).tv_gift_number.setText("x"+list.get(posiotn).goods_number);
if(list.get(posiotn).is_gift.equals("1")){
((ItemViewHolder) holder).tv_gift_tile.setText(list.get(posiotn).goods_name);
}else{
((ItemViewHolder) holder).tv_gift_tile.setText(list.get(posiotn).goods_name);
}
map.put(list.get(posiotn).goods_id, ((ItemViewHolder) holder).et_content);
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// TODO Auto-generated method stub
View view = LayoutInflater.from(context).inflate(
R.layout.item_goods_reviews,null);
return new ItemViewHolder(view);
}
class ItemViewHolder extends RecyclerView.ViewHolder
{
public ItemViewHolder(View itemView)
{
super(itemView);
// TODO Auto-generated constructor stub
ViewUtils.inject(this, itemView);
}
@ViewInject(R.id.iv_gift_img)
private ImageView iv_gift_img;
@ViewInject(R.id.tv_gift_tile)
private TextView tv_gift_tile;
@ViewInject(R.id.tv_gift_price)
private TextView tv_gift_price;
@ViewInject(R.id.tv_gift_number)
private TextView tv_gift_number;
@ViewInject(R.id.et_content)
private EditText et_content;
}
//供activity回调
public void getMap(IEvaluationMap iMap){
iMap.sendEvaluationData(map);
}
}
EvaluationListPresenter.java
public class EvaluationListPresenter extends BasePresenter
{
private String order_id;
private Context context;
private Handler handler;
private IEvaluation iView;
private static final int MEMBER_ORDER_CODE = 0x000002;
private static final int GOODS_COMMENTS_CODE = 0x000003;
@Override
public void initBaseData(Context context, Handler handler, Object iView,
Intent intent) {
this.context = context;
this.handler = handler;
if(iView instanceof IEvaluation){
this.iView = (IEvaluation) iView;
}
order_id = intent.getStringExtra("order_id");
}
@Override
public void initData() {
// TODO Auto-generated method stub
params.put("act", getActionName());
params.put("st", "detail");
params.put("order_id", order_id);
params.put("token", Constants.getInstance().token);
params.setSuccessMsgWhat(MEMBER_ORDER_CODE);
HttpTools.request(params, context, handler);
}
public void submit(String comment){
params = new RequestParams();
params.put("act", URLUtils.GOODS_COMMENTS);
params.put("st", "add");
params.put("order_id", order_id);
params.put("comment", comment);
params.put("token", Constants.getInstance().token);
params.setSuccessMsgWhat(GOODS_COMMENTS_CODE);
HttpTools.request(params, context, handler);
}
@Override
public String getActionName() {
// TODO Auto-generated method stub
return URLUtils.MEMBER_ORDER;
}
@Override
public void handMsg(Message msg) {
// TODO Auto-generated method stub
switch (msg.what)
{
case MEMBER_ORDER_CODE:
iView.sendData(JsonToModel.getJsonToModel(msg, OrderMainModel.class));
break;
case GOODS_COMMENTS_CODE:
//成功后的处理,此处省略
break;
default:
break;
}
}
}
OrderMainModel.java
public class OrderMainModel
{
public OrderInfoModel order_info;
public OrderMemberModel member_info;
public List<OrderPayModel> pay_info ;
public OrderShipModel ship_info;
public List<OrderGoodsModel> goods_list ;
}
IEvaluation.java
public interface IEvaluation
{
void sendData(OrderMainModel mModel);
}
EvaluationListToJsonModel.java
public class EvaluationListToJsonModel
{
public String goods_id;
public String comment;
}
以上是本次的实现代码,在adapter里面调用presenter,如果在adapter里面直接使用presenter,那么在初始化adapter的时候,就要从activity中传一个presenter到adapter,adapter中便可以直接调用presenter,这样可以实现我们需要的功能,但是这样做就使presenter和adapter两个原本不相关的两个类重新关联到一起,为了进一步解耦,通常我们使用activity实现一个为adapter量身打造的接口,为什么是量身打造?因为接口也是提倡单一职责原则,在初始化adapter的时候,把接口传过去,在adapter中通过调用接口来调用activity,在activity中再去调用presenter,这样,adapter可以与presenter交互的同时,两者也是尽可能少的知道对方。
好处:
- 解耦,不通过具体的类与类去实现,而是通过接口实现。
- 严格遵循MVP模式,adapter属于activity的一部分,主要职责是展示view,对于业务逻辑的处理,当时是放在P层。
本篇纯属个人见解,如有错误,欢迎批评指正。