以面向对象的角度分析抽象类和接口

21 篇文章 1 订阅

在这里插入图片描述

面向对象思想

面向对象编程即 OOP(Object Oriented Programming)
它有三大特征 :封装、继承、多态

封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
多态是同一个行为具有多个不同表现形式或形态的能力。

多态有2个体现,1个是方法的重载一个是方法的覆盖
面向对象思想相当于对万物的一个抽象,抽象具体体现在接口和抽象类

抽象类和接口

1.首先在定义上:
接口可以理解为特殊的抽象类;
抽象类和接口都是一个不完整的类,都没办法直接使用;
抽象类中既可以定义抽象方法、非抽象方法,也可以定义成员属性;
抽象类中的方法可以有方法体也可以没有方法体,只有方法名;
接口只可以定义方法名,接口中也可以定义属性,但是属性一定是public static final类型的。

另外,java 是单继承的,接口实际上是对单继承的补充。C++是有多继承的,为了能实现类的多继承,java中可以用接口来实现多继承的机制。

2.在语义上:
抽象类它针对的是行为和属性上进行抽象;
接口针对的是扩展的功能或能力,可以有这种能力也可以没有,
只要实现了接口就代表有这个能力,例如Java中Cloneable接口,实现Cloneable接口就表明这个类的对象是可以被克隆的。

3.现实使用的例子:
工作中的项目里将公共的方法向上抽取,成基类,比如BaseActivity,这个基类一般定义成抽象类,公共的功能向上抽取,写在基类BaseActivity中,需要不同页面实现的功能,需要定义成抽象方法,然后继承后重写,各自实现。比如各自页面的layout,如下面的例子

public abstract class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutRes());
        initView();
        getLayoutRes();
    }
   	//不同页面布局,需要定义成抽象方法,然后继承后重写
    protected abstract int getLayoutRes();
    protected abstract void initView();
}
public class TestActivity extends BaseActivity {
    

    @Override
    protected int getLayoutRes() {
        return R.layout.activity_test;
    }

    @Override
    protected void initView() {
        //todo 初始化view
    }
}

下面的例子讲解接口
接口主要表现在扩展能力上,比如地图定位并不是每个activty都使用,所以定义成一个接口,需要的时候来实现

//定义定位接口
interface LocationInterface {
	//初始化定位sdk
    void initLocation(Context context);
    //开始定位
    void startLocation(LocationCallback callback);
}

LocationCallback是定位结果的回调

interface LocationCallback {
    void callback(String adCode, String province, String city, String district);
}

基于高德地图的定位功能写一个实现类

public class AmapLocation implements LocationInterface{
    private AMapLocationClient locationClient;
    private LocationCallback callback;

    @Override
    public void initLocation(Context context) {
        //初始化client
        locationClient = new AMapLocationClient(context.getApplicationContext());
        AMapLocationClientOption locationOption = getDefaultOption();
        //设置定位参数
        locationClient.setLocationOption(locationOption);
        // 设置定位监听
        locationClient.setLocationListener(locationListener);
    }

    @Override
    public void startLocation(LocationCallback callback) {
        // 启动定位
        if (locationClient == null)
            return;
        if(callback == null)
            return;
        this.callback = callback;
        locationClient.startLocation();
    }

    /**
     * 定位监听
     */
    AMapLocationListener locationListener = new AMapLocationListener() {
        @Override
        public void onLocationChanged(AMapLocation location) {
            if (null != location) {

                StringBuffer sb = new StringBuffer();
//                //errCode等于0代表定位成功,其他的为定位失败,具体的可以参照官网定位错误码说明
                if (location.getErrorCode() == 0) {
                    //区域码
                    String adCode = location.getAdCode();

                    String province = location.getProvince();

                    String city = location.getCity();
                    String district = location.getDistrict();
                    callback.callback(adCode, province, city, district);
//                    updateAddress(adCode, province, city, district);
                } else {
                    //定位失败
                    sb.append("定位失败" + "\n");
                    sb.append("错误码:" + location.getErrorCode() + "\n");
                    sb.append("错误信息:" + location.getErrorInfo() + "\n");
                    sb.append("错误描述:" + location.getLocationDetail() + "\n");
                    //解析定位结果,
                    String result = sb.toString();
                    LogUtil.e(result);
                }
            } else {
                LogUtil.e("定位失败,loc is null");
            }
        }
    };

    public void locationOndestory(){
        if (null != locationClient) {
            /**
             * 如果AMapLocationClient是在当前Activity实例化的,
             * 在Activity的onDestroy中一定要执行AMapLocationClient的onDestroy
             */
            locationClient.onDestroy();
            locationClient = null;
            callback = null;
        }
    }

    /**
     * 默认的定位参数
     *
     * @author hongming.wang
     * @since 2.8.0
     */
    private AMapLocationClientOption getDefaultOption() {
        AMapLocationClientOption mOption = new AMapLocationClientOption();
        mOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);//可选,设置定位模式,可选的模式有高精度、仅设备、仅网络。默认为高精度模式
        mOption.setGpsFirst(false);//可选,设置是否gps优先,只在高精度模式下有效。默认关闭
        mOption.setHttpTimeOut(30000);//可选,设置网络请求超时时间。默认为30秒。在仅设备模式下无效
        mOption.setInterval(2000);//可选,设置定位间隔。默认为2秒
        mOption.setNeedAddress(true);//可选,设置是否返回逆地理地址信息。默认是true
        mOption.setOnceLocation(true);//可选,设置是否单次定位。默认是false
        mOption.setOnceLocationLatest(false);//可选,设置是否等待wifi刷新,默认为false.如果设置为true,会自动变为单次定位,持续定位时不要使用
        AMapLocationClientOption.setLocationProtocol(AMapLocationClientOption.AMapLocationProtocol.HTTP);//可选, 设置网络请求的协议。可选HTTP或者HTTPS。默认为HTTP
        mOption.setSensorEnable(false);//可选,设置是否使用传感器。默认是false
        mOption.setWifiScan(true); //可选,设置是否开启wifi扫描。默认为true,如果设置为false会同时停止主动刷新,停止以后完全依赖于系统刷新,定位位置可能存在误差
        mOption.setLocationCacheEnable(true); //可选,设置是否使用缓存定位,默认为true
        mOption.setGeoLanguage(AMapLocationClientOption.GeoLanguage.DEFAULT);//可选,设置逆地理信息的语言,默认值为默认语言(根据所在地区选择语言)
        return mOption;
    }
}

抽象一个BaseLocationActivity,使用到定位的Activity继承BaseLocationActivity

public abstract class BaseLocationActivity extends BaseActivity{

    protected void initLocation(LocationInterface locationInterface){
        locationInterface.initLocation(this);
    }

    protected void startLocation(LocationInterface locationInterface, LocationCallback callback){
        locationInterface.startLocation(callback);
    }

}

TestLocationActivity 继承BaseLocationActivity ,TestLocationActivity 就具备获取定位的功能,如果不想让其拥有定位功能可以继续继承BaseActivity

public class TestLocationActivity extends BaseLocationActivity {

    private AmapLocation amapLocation;

    @Override
    protected int getLayoutRes() {
        return R.layout.activity_test_location;
    }

    @Override
    protected void initView() {
        View tvLocation = findViewById(R.id.tv);
        amapLocation = new AmapLocation();
        initLocation(amapLocation);
        tvLocation.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
            	//开始定位
                startLocation(amapLocation,new MyLocationResult());
            }
        });
    }

    private class MyLocationResult implements LocationCallback{

        @Override
        public void callback(String adCode, String province, String city, String district) {
            Log.e("himi定位结果回调=========",adCode+province+city+district);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        amapLocation.locationOndestory();
    }
}
amapLocation = new AmapLocation();
initLocation(amapLocation);

TestLocationActivity 中的这段代码是控制反转的一个体现,initView()方法掌握着对AmapLocation的实现,使得BaseLocationActivity的initLocation方法将对AmapLocation的实例化转移到外部。

这样写会降低耦合性提高代码灵活性,如果因为某种原因将高德定位改为百度定位的话,也非常好改了,只需要再写一个BMapLocation类实现LocationInterface,然后创建BMapLocation对象,然后就可以再次通过initLocation和startLocation实现定位了,而不需要修改BaseLocationActivity 中的代码。

面向接口编程

我们对理想的编程通常概括为“高内聚,低耦合”,

内聚指的是专人专职,专门的类做专门的事;
在面向对象编程中,对象自身是内聚的,是保管好自己的数据,完成好自己的操作的,而对外界呈现出自己的状态和行为。一个对象往往不能干所有的事,都要与其它对象产生联系,一个对象和另一个对象产生依赖关系,也就是耦合。

我们在项目开发过程中,一般会用到依赖倒置原则(Dependence Inversion Principle,DIP)是指设计代码结构时,调用方不应该依赖被调用方,二者都应该依赖其抽象。举例来说就是,我去打印店打印,打印店如果只有黑白打印机,而我想打印彩色,这时,只能通过换一家打印店,或者让打印店再添加一台彩色打印机,这种现象就叫做调用方依赖被调用方。使用依赖倒置原则,我的打印店依赖其抽象,也就是打印机,我抽象一个打印机为接口,在打印方法中只需要传入打印机的实现类就能实现各种类型的打印了。

通过依赖倒置,可以减少类与类之间的耦合性,提高系统的稳定性,提高代码的可读性和可维护性,并且能够降低修改程序所造成的风险。
依赖倒置原则就是面向接口编程的一个体现。

面向对象编程其实更偏重于高内聚,面向接口更体现在低耦合
面向接口,就意味着面向抽象,作为哲学范畴而言,规定性少称为抽象,规定性多称为具体。而接口和抽象类,都是程序中的一种典型的“抽象”的形式,所以说面向接口编程目的是解耦,保证编程的灵活性。

另一篇文章《依赖倒置、控制反转、依赖注入、面向接口编程的理解》
参考
runoob:封装、继承、多态
那些年搞不懂的高深术语——依赖倒置•控制反转•依赖注入•面向接口编程
java开发——Cloneable接口、clone()方法和深浅拷贝

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值