【设计模式】Object Oriented面向对象思想剖析

来源:互联网 发布:大数据学习路线 编辑:程序博客网 时间:2024/06/09 21:23
OO思维(Object Oriented以对象为方向)
这里以"老张开车去东北"为例子。

创建一个ThinkInOO的工程:

先来看看小明的非面向对象思维的工程:
package cn.edu.ThinkInOO;public class Test1 {public static void main(String[] args) {System.out.println("老张开车去东北");}}


是不是没什么属性呢?于是他又改了:
package cn.edu.ThinkInOO;public class Test2 {public static void main(String[] args) {String driverName="老张";String vehicle="车";String tergetPlace="东北";System.out.println(driverName+"开"+vehicle+"去"+tergetPlace);}}


小刚问:“为什么连个方法也没有啊?”,小明想想也是,于是加了:
package cn.edu.ThinkInOO;public class Test2 {public static void main(String[] args) {String driverName="老张";String vehicle="车";String tergetPlace="东北";go( driverName,vehicle,tergetPlace);}public static void go(String driverName,String vehicle,String tergetPlace){System.out.println(driverName+"开"+vehicle+"去"+tergetPlace);}}
这样设计真的可以吗?答案肯定是否定的。

很多初学者设计程序都是非常非常简单,从来都是一个main方法就搞定了,就算他组织一个类,
很可能在一个类里一个方法200多行把所有功能全包括了,其实这就有点用面向过程出的思想去写
面向对象了。
要知道“方法”是封装的第一步。什么是封装?封装就是把一些功能放到特定的地方去,将来大
家调用的时候不需要去看里面的内容,只需要看类名、方法名就可以了。
封装是编程思想中复用的第一步。

面向对象的三个思想:封装、继承和多态。

回来看问题:定了半天,所有类都是Test,可不可以让它分解成类(class)呢?这就是面向对象的

第一步:

1.考虑类
名词:可以设置为类的属性,符合类条件的鲜明的特征都可以定义为类的名字。
下面我们开始进行"老张开车去东北"的类的封装:
我们封装3个类,分别是:Driver.java、Car.java、Address.java(司机、车型、地址)
Driver.java:
package cn.edu.ThinkInOO;public class Driver {}
Car.java:
package cn.edu.ThinkInOO;public class Car {}
Address.java:
package cn.edu.ThinkInOO;public class Address {}


2.属性
不可脱离具体的应用环境

定义完类之后,我们需要为每一个类定一些属性,对于任何一个类,都不能脱离具体的应用环境,

要根据具体的应用环境去顶我们的属性,跟我们没关系的属性我们不要去封装。

下面是这三个类的属性封装:
Driver.java:
package cn.edu.ThinkInOO;public class Driver {    String DriverName;}

Car.java:
package cn.edu.ThinkInOO;public class Car {    String carType;}


Address.java:
package cn.edu.ThinkInOO;public class Address {    String addressName;}

3.方法
下面我们来定义类的方法:
首先,司机肯定有一个开车的方法:
package cn.edu.ThinkInOO;public class Driver {String DriverName;public void driver(){}}


车有一个行驶的方法:
package cn.edu.ThinkInOO;public class Car {String Type;public void go(){}}

目的地类暂时没有方法


下面我们设置一些访问权限来保护属性的安全(private和get、set方法)
Car.java:
package cn.edu.ThinkInOO;public class Car extends Vihecle{private String Type;public String getType() {return Type;}public void setType(String Type) {this.Type = Type;}public void go(Address dest){}}
Driver.java:
package cn.edu.ThinkInOO;public class Driver {private String DriverName;public String getDriverName() {return DriverName;}public void setDriverName(String driverName) {DriverName = driverName;}public void driver() {}}
Address.java:
package cn.edu.ThinkInOO;public class Address {private String addressName;public String getAddressName() {return addressName;}public void setAddressName(String addressName) {this.addressName = addressName;}}


4.类之间的关系(依赖,继承,聚合)
写好了上面的东西,我们来执行"老张开车去东北"
写一个"Travel"(旅游)的测试类:
package cn.edu.ThinkInOO;public class Travel {public static void main(String[] args) {Driver d=new Driver();d.setDriverName("老张");d.driver();}}

写到这,就要考虑司机和车的关系了,driver老张和Car车如何联系?这就考虑类与类之间的关系了。


我们可以把Car设为Driver的一个私有变量,也可以将Car作为参数传入Driver的driver方法中。


这里我们采用后者:
package cn.edu.ThinkInOO;public class Driver {String DriverName;public void driver(Car c){}}


5.隐藏(封装)
降低耦合度。
什么叫耦合度?假如你盖了个房子,现在想在房子旁边盖一个配房,如果你盖配房的时候需要把
主房的门窗柱子拆下来改,这说明原来房子设计的不好。哪不好?新的功能和老的功能之间的耦
合度太强了,耦合就是连在一起的,牵一发而动全身,我要添加新功能的时候,我居然要把原来
的功能改一遍,说明设计的不好。我要把窗户从方的改成圆的我就要把所有墙重新做,说明耦合
度太强了。这样做肯定不合理,改窗户只和窗户或那面墙有关系,耦合度不可能100%,但是我们可

以将耦合度降到合适的程度。


所以在Driver类中的driver方法里,我们传入了Car,对于Car如何行使,我们只在Car类中去设置,而和Driver类没有任何关系

package cn.edu.ThinkInOO;public class Driver {String DriverName;public void driver(Car c){    c.go();}}
属性private,读取用get与set是隐藏与封装,
尽量把对方该做的事情让对方自己去做这也是隐藏与封装。


6.站在使用者的角度思考
我们开车要去哪呢?地址可以设置成Car的go方法的参数:
package cn.edu.ThinkInOO;public class Car {private String Type;public String getType() {return Type;}public void setType(String Type) {this.Type = Type;}public void go(Address dest){System.out.println(Type+"一路加着速,疾驰至"+dest.getAddressName());}}
给Address加了构造方法来传参数:
package cn.edu.ThinkInOO;public class Address {private String addressName;Address(){}Address(String dest){this.setAddressName(dest);}public String getAddressName() {return addressName;}public void setAddressName(String addressName) {this.addressName = addressName;}}


司机类:
package cn.edu.ThinkInOO;public class Driver {private String DriverName;public String getDriverName() {return DriverName;}public void setDriverName(String driverName) {DriverName = driverName;}public void driver(Car c){c.go(new Address("东北"));}}
测试类;
package cn.edu.ThinkInOO;public class Travel {public static void main(String[] args) {Driver d=new Driver();d.setDriverName("老张");d.driver(new Car());}}

但是你有没有发现,我们无法从测试程序告知司机目的地是哪,只能指定开什么车去。
这就需要当你设计一个类的方法,应该站在使用者的角度去设计,去考虑需求。

那么类就需要"扩展"。
扩展后的Driver:
package cn.edu.ThinkInOO;public class Driver {private String DriverName;public String getDriverName() {return DriverName;}public void setDriverName(String driverName) {DriverName = driverName;}public void driver(Car c){c.go(new Address("东北"));}public void driver(Car c,Address dest){c.go(dest);}}
“重载”了driver方法,加了目的地的属性。


6.继承
如果老张开飞机去不开汽车去怎么办?
小明第一个想到的就是再写一个Plane.java的类,在重载driver方法,给它加上Plane的参数,并
完成目的地的指定。这种方法没有问题,可以这么设计,但是,有没有更好的设计方法呢?如果
我开轮船呢?做滑翔机呢?难道每次都要重写一个类,然后重载driver方法,然后重新指定
driver的参数?那样不仅麻烦,而且代码冗余度很高。

这里我们可以用继承的方式来解决这个问题,提升程序的拓展性。
我们为所有交通工具设置一个父类交通工具类Vihecle.java:
package cn.edu.ThinkInOO;public class Vihecle {}
我们写一个飞机类继承Vihecle,并把Car也继承Vihecle。
package cn.edu.ThinkInOO;public class Plane extends Vihecle{private String Type;public String getType() {return Type;}public void setType(String Type) {this.Type = Type;}public void go(Address dest){System.out.println(planeType+"一路转着螺旋桨,翱翔至"+dest.getAddressName());}}

Car.java:
package cn.edu.ThinkInOO;public class Car extends Vihecle{private String Type;public String getType() {return Type;}public void setType(String Type) {this.Type = Type;}public void go(Address dest){System.out.println(carType+"一路加着速,疾驰至"+dest.getAddressName());}}

在driver中就可以这么写:
package cn.edu.ThinkInOO;public class Driver {private String DriverName;public String getDriverName() {return DriverName;}public void setDriverName(String driverName) {DriverName = driverName;}public void driver(Vihecle v,Address dest){}}
只需要一个driver方法即可满足所有交通工具。
只要你能说通这句话,就可以考虑使用继承:"什么什么是一种什么什么"
继承关系由于耦合度非常强,应该谨慎使用。


7.多态(核心)
好处:可拓展性
三个特性:有继承、有重写、有父类引用指向子类对象。
多态是什么意思呢?上面我们定义了driver方法,但是没有写方法内容,我们如何使用传入进来
的Vihecle类?如果判断类型的话,拓展性依旧没变化。我们可以这样:
在Vihecle里定义一个go方法。
那么go方法如何让实现呢?不知道工具类型,所以不需要实现。
我们把方法和类设置成抽象(abstract)的
package cn.edu.ThinkInOO;public abstract class Vihecle {private String Type;public String getType() {return Type;}public void setType(String Type) {this.Type = Type;}public abstract void go(Address dest);}
Driver类:
package cn.edu.ThinkInOO;public class Driver {private String DriverName;public String getDriverName() {return DriverName;}public void setDriverName(String driverName) {DriverName = driverName;}public void driver(Vihecle v,Address dest){        System.out.print(DriverName+"驾驶着");v.go(dest);}}


那么Car与Plane都继承了Vihecle类,都重写了Vihecle的go方法。
那么当我们向Driver类的driver方法传递任何Vihecle的子类的时候,你传的是谁,就调用的谁的


go方法,这就是多态。
测试:
package cn.edu.ThinkInOO;public class Travel {public static void main(String[] args) {Driver d=new Driver();d.setDriverName("老张");Vihecle c=new Car();c.setType("法拉利");d.driver(c,new Address("东北"));d.setDriverName("老李");Vihecle p=new Plane();p.setType("战斗机");d.driver(p,new Address("美国"));}}

测试结果:
老张驾驶着法拉利一路加着速,疾驰至东北
老李驾驶着战斗机一路转着螺旋桨,翱翔至美国

至此,面向对象的思想全部介绍完毕!

注意:
1.设计没有绝对的对与错
2.Over Design也是一种罪过
3.没有任何实际中的设计会一步到位

4.初学者不要考虑太多的原则和条条框框,最重要是动手写


举个例子:抽象类与接口的区别:
如果你是考虑一些有共同特征的事物,我们就设计接口。
如果你对特征比较模糊,就用抽象类。
而且类一次只能继承一个类,而可以实现多个接口。

5.享受编程的乐趣吧

结语:
O O 思想慢慢来
封装继承和多态
设计层层无止境
适可而止乐开怀


留的作业:
农场一头小母牛,
每年生头小母牛,
母牛五岁产母牛,
二十年上多少牛?
请用面向对象的思想来解决这个问题。
我写的母牛作业在下面这篇文章里:http://blog.csdn.net/acmman/article/details/43817323

知识点总结于马士兵老师的"设计模式"视频,感谢尚学堂的共享。

转载请注明出处:http://blog.csdn.net/acmman

0 0
原创粉丝点击