面向对象五大基本原则(二)
来源:互联网 发布:中国高铁 知乎 编辑:程序博客网 时间:2024/05/29 04:49
前言
时间如梭,少年仍在奔跑!!!
Ⅰ.里氏替换原则
简述:关于里氏替换原则,可能在每天的代码中都有出现关于这一原则的使用,只是一直都在使用,而没有意识到这就是所谓的里氏替换原则。里氏替换原则的思想是:”基类可实现的功能,子类也可以实现”,
下面代码,假设有一个List参数的方法C,里面的逻辑是根据索引找到相应的集合元素,那么当需要list的实现类的索引查找功能时,可以将list的实现类(ArrayList、LinkedList等)任意一个作为方法C的参数传入,类似这样的代码可能每天都会遇到,可这就是里氏替换原则的体现;
public String C(List<String> contents,int index){ return contents.get(index); }
下面代码,接着假设有一个方法D声明的返回类型是List,可下面代码真实返回的类型却是ArrayList,而调用者并不知道返回的类型是ArrayList,只知道给其返回了List,这也是里氏替换原则的体现;
public List<Integer> D(){ //...省略 //ArrayList<Integer> ids = new ArrayList<Integer>; return id;}
上面列举的两个例子都是集合中接口和实现类之间的关系,也就是基类与子类的关系。基类可以实现的功能,子类可以代替基类实现相应的功能,这也是java面向对象特性的体现,所以在java语言里,更实在的体现出了里氏替换原则。其实在开发中的代码,里氏替换原则应该是随处可见的,再看看下面的代码,是不是跟上面都同样的体现;
//主函数 class App{ public static void main(String args[]){ ArrayList<String> str = getHappyNeyYear(); NewYearFactory newYearFactory = new NewNewYearFactoryImpl(); List<Integer> year = newYearFactory.createNewYear(str); } } //演示接口 interface NewYearFactory{ List<Integer> createNewYear(List<String> strContent); } //实现类 class NewYearFactoryImpl implements NewYearFactory{ @Override public List<Integer> createNewYear(List<String> strContent) { ArrayList<Integer> year = api.getYear(strContent); return year; } }
总结:基类可实现的功能,子类同样可以实现,在继承或实现中,子类延续了基类的特性。
Ⅱ.接口隔离原则
简述:接口隔离原则的核心思想是要求接口不要过于通用,须追求专一的功能.
假设下面Hobit接口是提供给开发者选择爱好的,而刚好A同学的爱好是逛街,那么A同学是不是就要重写Hobit接口,接着实现shoppting函数的逻辑代码.
public interface Hobit{ void coding(); void readBook(); void shopping(); } public static void doHobit(){ Hobit hobit = new Hobit() { @Override public void coding() { } @Override public void readBook() { } @Override public void shopping() { //... } }; }
上面假设A同学的爱好是逛街,可在实现Hobit接口的时候却也得实现coding和readBook这两个方法,这不是多余吗?那么可以去掉这两个方法吗? 假如去掉这两个方法,这时候刚好B同学的爱好是读书,那么Hobit接口没有了readBook这个方法,又该怎么办呢?
上面的假设会不会觉得貌似很矛盾,接口太通用,会觉得出现多余的代码;接口太专一,那多出来的功能又该怎么实现呢?如果你熟悉Android关于设置控件(比如TextView、Button等)的点击事件/长按事件/触摸事件,或许你会恍然大悟,看看下面的代码,怎么实现设置控件的点击事件
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { }});button.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { return false; }});button.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return false; }});
点击事件由OnClickListener接口负责,长按事件由OnLongClickListener接口负责,触摸事件由OnTouchListener接口负责,开发者要处理什么事件,只需要实现相应的接口。这样的话不是可以解决上面出现的两个问题
- 接口通用导致代码多余的问题;
- 接口专一导致功能欠缺的问题.
接着再看看下面关于Android属性动画监听的接口实现,是不是觉得Android系统关于实现动画的监听怎么违背了接口隔离原则,实现动画监听接口,还得重写里面的四个方法,太多余了吧!其实可能Android系统开发者当初在设计这个接口的时候,考虑到开发者可能需要同时使用到其中几个方法,所以一并提供了.当然,这也有好处,也是有坏处的,为了完善这个接口,Android系统开发者也重新提供了另一接口AnimatorListenerAdapter,在该接口中已对AnimatorListener的方法进行了空实现,开发者只需要重写所需的方法即可.
Button button = new Button(this); ObjectAnimator animator = ObjectAnimator.ofFloat(button, "rotationX", 0, 360); animator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { }});
总结:在开发中,应追求专一接口,这样可以避免很多的问题;那么假设你在开发项目的第一版本时,写了上面的Hobit接口,当某天由于需求变更而得去增加爱好选项,那么这时是否将增加的爱好选项添加到Hobit接口里,这样是不是会导致项目之前所有实现Hobit接口的地方都得进行更改,这样就麻烦~~,所以专一接口是能避免很多问题的
附加:上面Hobit接口关于爱好的,或许列举得不是很好,但大致了解就好.
Ⅲ.依赖倒置原则
在传统软件的开发中,通常是接口或抽象类依赖于具体类,当具体类有变动,那么就得改变其接口或抽象类,而依赖倒置原则的出现,正是将这一传统的依赖倒置过来,使得具体类依赖于接口或抽象类。
现在假设有这么一需求,用户输入信息后点击提交,系统负责读取、校验和永久存储,下面是Android代码实现:
findViewById(R.id.tv_submit).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String userInput = mEditText.getText().toString(); if(!TextUtils.isEmpty(userInput) && inputNews.check(userInput)){ //验证输入的数据是否符合业务逻辑 SQLiteDatabase db = new DbOpenHelper(ProviderActivity01.this).getWritableDatabase(); ContentValues contentValues = new ContentValues(); contentValues.put("input", userInput); db.insert("user", null, contentValues); }else{ Toast.makeText(ProviderActivity01.this,"抱歉,你输入的数据不合法",Toast.LENGTH_SHORT).show(); } }});
一个普遍的现象,高层级模块依赖于底层级模块,比如上面代码中的UI层依赖业务层,业务层依赖数据层。那么如何用抽象来实现依赖倒置原则,解决上面高层模块依赖底层模块的现象呢?另一方面,我们也不想要一个简单的完整的类来完成所有的事情,这时就可以想到单一职责原则,将各个职能进行划分,来优化上面的代码.
数据层
public interface DataLayer { void insert(String value); } /**实现类*/public class DataLayerImpl implements DataLayer{ private Context mContext; public DataLayerImpl(Context context){ mContext = context; } @Override public void insert(String value){ SQLiteDatabase db = new DbOpenHelper(mContext).getWritableDatabase(); ContentValues contentValues = new ContentValues(); contentValues.put("input",value); db.insert("user",null,contentValues); } }
业务层
public interface BusinessLayer { void check(String str); } /**实现类*/public class BusinessLayerImpl implements BusinessLayer { private DataLayer mDataLayer; public BusinessLayerImpl(DataLayer mDataLayer) { this.mDataLayer = mDataLayer; } @Override public void check(String str){ //...业务逻辑校验,略 mDataLayer.insert(str); } }
UI层
public class ProviderActivity extends AppCompatActivity { private EditText mEditText; private DataLayer mDataLayer; private BusinessLayer mBusinessLayer; @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_provider); initView(); findViewById(R.id.tv_submit).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String userInput = mEditText.getText().toString(); if(!TextUtils.isEmpty(userInput)){ mDataLayer = new DataLayerImpl(ProviderActivity01.this); mBusinessLayer = new BusinessLayerImpl(dataLayer); mBusinessLayer.check(userInput); }else{ Toast.makeText(ProviderActivity01.this,"抱歉,你输入的数据不合法",Toast.LENGTH_SHORT).show(); } } }); }
数据层和业务层的解耦,通过接口和构造注入解决;在面向对象的世界里,类与类之间可以有这么几种关系:
- 零耦合:表现在两个类之间没有任何耦合关系;
- 具体耦合:表现在一个类对另一个类的直接引用;
- 抽象耦合:表现在一个具体类和一个抽象类之间;
那上面的伪代码还是主要体现在了抽象耦合,通过对业务层的构造函数传入数据层的接口,这样久使得依赖关系存在了最大的灵活性。最后,我们高层级的模块都依赖于抽象了(接口)。更进一步,我们的抽象不依赖于细节,它们也依赖于抽象。
总结上面的伪代码,UI 层是依赖于业务逻辑层的接口的,业务逻辑层的接口依赖于数据层的接口.
总结:依赖倒置原则的使用前提得看场景,其具体的体现是在抽象类型上,不要为了抽象而去抽象,一些相对稳定、保持不变的类也没有使用的必要.
Ⅳ.总结…继续…
- 面向对象五大基本原则(二)
- 面向对象五大基本原则
- 面向对象五大基本原则
- 面向对象五大基本原则
- 面向对象五大基本原则
- 面向对象五大基本原则
- 面向对象五大基本原则
- 面向对象五大基本原则
- 面向对象五大基本原则
- 面向对象五大基本原则
- 面向对象五大基本原则
- 面向对象五大基本原则
- 面向对象五大基本原则
- 面向对象设计的五大基本原则
- 面向对象设计五大基本原则
- 面向对象之五大基本原则
- 面向对象的五大基本原则-solid
- 面向对象的五大基本原则
- solr 学习之路 如何将数据库中大量的数据导入solr <三>
- java多线程总结2——Lock
- 详解 HTTP与HTTPS
- 设计模式(8) ------- 桥接模式
- Android复习笔记
- 面向对象五大基本原则(二)
- ubuntu 虚拟机上不了网 主机能上网
- cdoj 1131 男神的礼物 区间dp
- String创建方式内存终极分析
- 仿QQ音乐播放界面(已实现主要功能)
- Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean wi
- string stringbuffer stringbuilder区别
- 关于office work老弹出宏错误
- tensorflow | 操作函数 和 数据类型