您的位置:首页 > 移动开发 > Android开发

分享课题之—设计的思维转变(面向对象开发的六大原则)

2018-03-04 18:26 399 查看
这是我在工作中给其他员工分享过的一个课题,在这里也分享一样,希望对各个新开发人员也有帮助

引入思考当我们要实现这样一项功能时,我们的做法是怎样的,功能:我坐车去公司

最元始的做法,直接在Activity里实现public class MainActivity extends Activity{

@Override

protectedvoid onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

go();

}

privatevoid go() {

//TODO我坐车去公司

}

}但这会遇上一个问题,万一有一天不是我,而是我同事,不是去公司,而是去酒店,这时候又怎么办,这时我们要对代码做这样的改变:
class Person{
private String name;

publicvoidsetName(String name){
this.name = name;
}

public String getName(){
return name;
}

public void go(Car car,Address address){
car.drive(address);
}
}
class Address{
private String name;

public void setName(String name){
this.name = name;
}

public String getName(){
return name;
}
}
class Car{
public void drive(Address address){
//TODO  go address
}
}

然后在MainActivity里:public MainActivity extends Activity{

@Override
protectedvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Person person = new Person();
person.setName("同事");
           Addressaddress = new Address();
     address.setName("酒店");
         Carcar = new Car();
         person.go(car,address);
}
}
这样看上去就好多了,起码可以扩展了,我想谁去就谁去,想去哪就去哪,这个就是一个封装的过程,也是面象对象的一个重要特性

可能,我们很多少写代码也限于此了,因为这看上去的确挺完美的了,但突然老板发话了,不单你要去公司,你养的那只旺财也要去公司,这时你就懵了,怎么还有这样的客户,这都什么需求,算,改,于是你再新建一个Dog的类,类里再写个go方法,但如果更变态,让你家的所有的动物都去公司呢,而且是旺财坐飞机,猫骑单车,鸡过船去(就这么变态),好,再建堆动物的类,再建一堆交通工具的类,然后再写go和drive方法,好像可以实现。

但你们有没有发现,现在Person的go方法和Car绑定的,同理,旺财的go是和飞机绑定的,这样的话,如果要旺财改骑单车,鸡改开车,这时代码就基本全部要改过来,这样的代码就是偶合性强,牵一发而动全身。
所以我们能不能做这样一件事情,什么动物以什么样的方式去哪里都可以根据需要进行拓展,而不是只能是具体的某一样,与是我们可以换个思考问题的模式了,既然是会动的都可以去,是交通工具都可以乘,我们何不把它们抽象出来,具体的实现由要具体的动物和交通工具实现,于是就有可能变成这样:
定义抽象的动物和交通工具类
abstractclass Animal{
abstractvoid go(Vehicle vehicle,Address address);
}
abstractclass Vehicle{
abstractvoiddirve(Address address);
}
谁想去就要实现动物类,不然就不能去,就规定了只有动物能去
class Person extends Animal{
private String name;
publicvoid setName(String name){
this.name = name;
}
public String getName(){
return name;
}
@Override
void go(Vehicle vehicle,Addressaddress) {
// TODO Auto-generated method stub
vehicle.dirve(address);
}}
具体交通工具也是要实现交通工具类才能搭动物去其它地方
class Car extends Vehicle{
@Override
voiddirve(Address address) {
// TODO开去 address (具体实现)
}
}
class Bike extends Vehicle{
@Override
voiddirve(Address address) {
// TODO骑去 address (具体实现)
}}
class Address{
private String name;
publicvoidsetName(String name){
this.name = name;
}
public String getName(){
return name;
}
}

然后在Main方法里
publicclassMainActivityextends Activity{
@Override
protectedvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Person person = new Person();
person.setName("同事的同事");
Vehicle vehicle = new Car();(类似collection里的list,map等)
Address address = new Address();
address.setName("公司");
person.go(vehicle, address);
}

这样,就实现了无论谁要坐什么去哪里,只要实现了对应的接口,自己实现方法就可以了,当然,这个例子只是个引子,从中我们可能看出一点设计的思想和原则,现在我们先讨论一下开发的一些原则,至于设计的思想(设计模式)的东西,以后有空再来讨论

1.OCP (Open-ClosedPrinciple)开放-封闭原则
通俗的说,就是我们写的东西应该是,对扩展开放,对修改关闭(当然,这里指的不是改BUG)。 就是我们写好的功能和模块当中,如果要增加新的功能,只需要在原有基础上附加新的模块,不需要改动原来的代码,或者做小小的接合性的改动,就能实现功能的新增,而不是改动原有的模块,我们知道,改动代码,特别是功能性的代码的时候,往往会有不可预测的问题,如果要改动原有模块来新增模块的话,以前已经稳定的模块又打散了,又要全部功能再做测试(测试的成本非常大的)。比如之前的例子,如果Person类里的go()里传的是具体的某一种交通工具,那要让Person再能用另一种交通工具出行,那Person里就要再加个go()方法传的是另一种,这样Person的稳定打破了,这样就要做新的功能和Person的修改与测试。不单代码冗余还不好扩展。

2. LSP(“LiskovSubstitution Principle”) 里氏代换原则 
如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。 这个也是多态的一个体现,完全可以由子类代替父类,这样做的好处也是易于扩展,只要新建子类继承,再由子类去完成要扩展的功能,要求,尽量不要复写父类的方法,即使是复写,也不要完成功能有冲突的功能。

继承作为面向对象三大特性之一,在给程序设计带来巨大便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加了对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能会产生故障。 

里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。
3.DIP (Dependence Inversion Principle)依赖倒置原则

高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。在java中,抽象指的是接口或者抽象类,细节就是具体的实现类,使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。

4.ISP 
全称:“InterfaceSegregation Principle” 接口隔离原则 说明:使用多个专一功能的接口比使用一个的总接口总要好。从一个客户类的角度来讲:一个类对另外一个类的依赖性应当是建立在最小接口上的。过于臃肿的接口是对接口的污染,不应该强迫客户依赖于它们不用的方法。 



5.CARP or CRP 

全称:“Composite/AggregateReuse Principle” 合成/聚合复用原则or“Composite Reuse Principle” 合成复用原则 
说明:如果新对象的某些功能在别的已经创建好的对象里面已经实现,那么尽量使用别的对象提供的功能,使之成为新对象的一部分,而不要自己再重新创建。新对象通过向这些对象的委派达到复用已有功能的。 简而言之,要尽量使用合成/聚合,尽量不要使用继承。

使用继承复用会破坏系统的封装性。因为继承会将基类的实现暴露给子类。如果基类发生变化,那么子类的实现也不得不发生变化。从基类继承而来的实现是静态的。不可能在运行时发生改变,没有足够的灵活性。所以继承又叫“白箱复用”由于组合或聚合关系可以将已有的对象(也可称为成员对象)纳入到新对象中,使之成为新对象的一部分,因此新对象可以调用已有对象的功能,这样做可以使得成员对象的内部实现细节对于新对象不可见,所以这种复用又称为“黑箱”复用。
6.LOD or LKP 

全称:“Lawof Demeter” 迪米特原则or“Least Knowledge Principle”最少知识原则 说明:对象与对象之间应该使用尽可能少的方法来关联,避免千丝万缕的关系。 如何实现迪米特法则 定义:一个对象应该对其他对象保持最少的了解。问题由来:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
除了这6个,还有一个单一职责原则,这里也说明一下,
SRP(SRP--Single-ResponsibilityPrinciple):就一个类而言,应该只专注于做一件事和仅有一个引起它变化的原因。所谓职责,我们可以理解他为功能,就是设计的这个类功能应该只有一个,而不是两个或更多。也可以理解为引用变化的原因,当你发现有两个变化会要求我们修改这个类,那么你就要考虑撤分这个类了。因为职责是变化的一个轴线,当需求变化时,该变化会反映类的职责的变化

定义:不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。问题由来:类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。
解决方案:遵循单一职责原则。分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1时,不会使职责P2发生故障风险;同理,当修改T2时,也不会使职责P1发生故障风险。

就比如一个公司有一个员工是有多项职责的(app开发和游戏开发),突然有一天,公司有一天要派app开发的去出差,这时发现连游戏开发的也不能进行了。

我们要转变我们的思维方式,站在老板的角度想问题,先定义宏观的方向,也就是要实现的目标,从产品的方面说,就是产品要实现的功能,然后老大就对下面的人说,不管你用什么方法,你就帮我实现这样的功能,而不是说,你帮我这样实现这个功能,这样的话即使有一天不要你了,请了第二个人,第二个人一看,也知道老板要实现这样的东东,然后巴啦巴啦的就写出来的。( 当然有些人就不想以后的人看得懂他的代码,这种思想,有点可怕)

一个项目就像一个公司,部门分工明确而不是一个部门就完成所有公司的事项,部门间相对独立,不会因为一个部门内部出了点问题而影响到其它所有部门,老大相对稳定,所以下面的员工都依赖老大们而不是依赖员工,公司老大也是员工之一,所以可以直接用老大执行员工的事务而不能由员工执行老大的事务,
这是本人简单分享过的,也是为了能更好引导大家学习一下怎样写出更好的代码,主要还是要多学习和实验一下才有进步。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息