OO思维(Object Oriented以对象为方向)
这里以"老张开车去东北"为例子。
创建一个ThinkInOO的工程:
先来看看小明的非面向对象思维的工程:
是不是没什么属性呢?于是他又改了:
小刚问:“为什么连个方法也没有啊?”,小明想想也是,于是加了:
很多初学者设计程序都是非常非常简单,从来都是一个main方法就搞定了,就算他组织一个类,
很可能在一个类里一个方法200多行把所有功能全包括了,其实这就有点用面向过程出的思想去写
面向对象了。
要知道“方法”是封装的第一步。什么是封装?封装就是把一些功能放到特定的地方去,将来大
家调用的时候不需要去看里面的内容,只需要看类名、方法名就可以了。
封装是编程思想中复用的第一步。
面向对象的三个思想:封装、继承和多态。
回来看问题:定了半天,所有类都是Test,可不可以让它分解成类(class)呢?这就是面向对象的
第一步:
1.考虑类
名词:可以设置为类的属性,符合类条件的鲜明的特征都可以定义为类的名字。
下面我们开始进行"老张开车去东北"的类的封装:
我们封装3个类,分别是:Driver.java、Car.java、Address.java(司机、车型、地址)
Driver.java:
2.属性
不可脱离具体的应用环境
Driver.java:
Car.java:
Address.java:
3.方法
下面我们来定义类的方法:
首先,司机肯定有一个开车的方法:
车有一个行驶的方法:
目的地类暂时没有方法
下面我们设置一些访问权限来保护属性的安全(private和get、set方法)
Car.java:
4.类之间的关系(依赖,继承,聚合)
写好了上面的东西,我们来执行"老张开车去东北"
写一个"Travel"(旅游)的测试类:
这里我们采用后者:
5.隐藏(封装)
降低耦合度。
什么叫耦合度?假如你盖了个房子,现在想在房子旁边盖一个配房,如果你盖配房的时候需要把
主房的门窗柱子拆下来改,这说明原来房子设计的不好。哪不好?新的功能和老的功能之间的耦
合度太强了,耦合就是连在一起的,牵一发而动全身,我要添加新功能的时候,我居然要把原来
的功能改一遍,说明设计的不好。我要把窗户从方的改成圆的我就要把所有墙重新做,说明耦合
度太强了。这样做肯定不合理,改窗户只和窗户或那面墙有关系,耦合度不可能100%,但是我们可
尽量把对方该做的事情让对方自己去做这也是隐藏与封装。
6.站在使用者的角度思考
我们开车要去哪呢?地址可以设置成Car的go方法的参数:
司机类:
但是你有没有发现,我们无法从测试程序告知司机目的地是哪,只能指定开什么车去。
这就需要当你设计一个类的方法,应该站在使用者的角度去设计,去考虑需求。
那么类就需要"扩展"。
扩展后的Driver:
6.继承
如果老张开飞机去不开汽车去怎么办?
小明第一个想到的就是再写一个Plane.java的类,在重载driver方法,给它加上Plane的参数,并
完成目的地的指定。这种方法没有问题,可以这么设计,但是,有没有更好的设计方法呢?如果
我开轮船呢?做滑翔机呢?难道每次都要重写一个类,然后重载driver方法,然后重新指定
driver的参数?那样不仅麻烦,而且代码冗余度很高。
这里我们可以用继承的方式来解决这个问题,提升程序的拓展性。
我们为所有交通工具设置一个父类交通工具类Vihecle.java:
Car.java:
在driver中就可以这么写:
只要你能说通这句话,就可以考虑使用继承:"什么什么是一种什么什么"
继承关系由于耦合度非常强,应该谨慎使用。
7.多态(核心)
好处:可拓展性
三个特性:有继承、有重写、有父类引用指向子类对象。
多态是什么意思呢?上面我们定义了driver方法,但是没有写方法内容,我们如何使用传入进来
的Vihecle类?如果判断类型的话,拓展性依旧没变化。我们可以这样:
在Vihecle里定义一个go方法。
那么go方法如何让实现呢?不知道工具类型,所以不需要实现。
我们把方法和类设置成抽象(abstract)的
那么Car与Plane都继承了Vihecle类,都重写了Vihecle的go方法。
那么当我们向Driver类的driver方法传递任何Vihecle的子类的时候,你传的是谁,就调用的谁的
go方法,这就是多态。
测试:
测试结果:
老张驾驶着法拉利一路加着速,疾驰至东北
老李驾驶着战斗机一路转着螺旋桨,翱翔至美国
至此,面向对象的思想全部介绍完毕!
注意:
1.设计没有绝对的对与错
2.Over Design也是一种罪过
3.没有任何实际中的设计会一步到位
如果你是考虑一些有共同特征的事物,我们就设计接口。
如果你对特征比较模糊,就用抽象类。
而且类一次只能继承一个类,而可以实现多个接口。
5.享受编程的乐趣吧
结语:
O O 思想慢慢来
封装继承和多态
设计层层无止境
适可而止乐开怀
留的作业:
农场一头小母牛,
每年生头小母牛,
母牛五岁产母牛,
二十年上多少牛?
请用面向对象的思想来解决这个问题。
我写的母牛作业在下面这篇文章里: http://blog.csdn.net/acmman/article/details/43817323
这里以"老张开车去东北"为例子。
创建一个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车如何联系?这就考虑类与类之间的关系了。
这里我们采用后者:
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