Java设计形式——面向对象的几个基本原则
1. 面向抽象原则
1.1 抽象类
特点:
1.抽象类中的abstract方法可有可无,也可以有非abstract方法
2.抽象类不能用new创建对象
3.抽象类的非抽象子类必须重写父类的abstract方法
4.作为向上转型对象。抽象类不能创建对象,但可以让抽象类的对象成为其非抽象子类的向上转型对象,调用子类的方法
abstract class A { public abstract int add(int x,int y); } class B extends A { public int add(int x, int y) { return x+y; } } public class App { public static void main(String[] args) { A a = new B(); System.out.println(a.add(3, 4)); } }
1.2 接口
特点:1.接口中只能有public权限的abstract方法,不能有非abstract方法
2.接口由类去实现,并且必须重写接口中的abstract方法
3.接口回调。把实现接口的类的对象的引用赋给该接口声明的接口变量,那么此接口变量就可以调用被类实现的接口中的方法
interface Com { public abstract int sub(int x,int y); } class ComImp implements Com { public int sub(int x, int y) { return x-y; } } public class App { public static void main(String[] args) { Com com = new ComImp(); System.out.println(com.sub(3, 4)); } }
1.3 面向抽象
所谓面向抽象编程,就是指当设计一个类时,不该让类面向具体的类,而是面向抽象类或接口,即所设计的类中的重要数据是抽象类或接口声明的变量,而不是具体声明的变量。例如,现在有一个Circle类,有一个getArea()方法计算圆的面积:
Circle.java
public class Circle { double r; Circle(double r) { this.r = r; } public double getArea() { return(3.14*r*r); } }
现在要设计一个Pillar类(柱体类),有一个getVolume方法计算体积
Pillar.java
public class Pillar { Circle bottom; //将Circle对象作为成员 double height; public Pillar(Circle bottom, double height) { this.bottom = bottom; this.height = height; } public double getVolume() { return bottom.getArea()*height; } }现在来分析Pillar类,bottom是用具体类Circle类声明的变量,若用户需求不变化,则Pillar类可以这样设计。但如果用户希望求底是三角形的柱体的体积,显然上述Pillar类不能应对这种需求。
因此需要重新设计Pillar类,这时不需要关心底是什么形状,而应该关心底是否有计算面积的方法。所以底不应该是某个具体类声明的变量,这样才能应对不同的需求。
接下来重新设计PIllar类。 首先设计一个抽象类(或接口)Geometry(几何),有一个抽象的getArea()方法。
Geometry.java
public abstract class Geometry { public abstract double getArea(); }这样设计以后,Pillar类就不再依赖具体类,而是面向Geometry类,即Pillar类中的bottom是抽象类Geometry声明的变量。
Pillar.java
public class Pillar { Geometry bottom; //bottom是抽象类Geometry声明的变量 double height; public Pillar(Geometry bottom, double height) { this.bottom = bottom; this.height = height; } public double getVolume() { return bottom.getArea()*height; } }接下来就是让Circle和Rectangle(矩形)继承抽象类Geometry,并重写getArea()方法。
Circle.java
public class Circle extends Geometry { double r; Circle(double r) { this.r = r; } public double getArea() { return(3.14*r*r); } }
Rectangle.java
public class Rectangle extends Geometry { double length; double width; public Rectangle(double length, double width) { this.length = length; this.width = width; } public double getArea() { return length*width; } }
现在就可以用Pillar创建具有矩形底或圆形底的柱体了。
App.java
public class App { public static void main(String[] args) { Pillar pillar; Geometry bottom; bottom = new Rectangle(100, 50); pillar = new Pillar(bottom, 50); System.out.println("矩形底的柱体的体积:" + pillar.getVolume()); bottom = new Circle(50); pillar = new Pillar(bottom, 50); System.out.println("圆形底的柱体的体积:" + pillar.getVolume()); } }
通过面向抽象来设计Pillar类,使得Pillar类不再依赖具体类,因此每当系统增加Geometry的子类时,比如增加一个Triabgle子类,那么不需要修改Pillar类的任何代码,就可以使用Pillar创建出具有三角形底的柱体。
2. 开-闭原则
所谓“开-闭原则“(Open-Closed Principle)就是让设计对扩展开放,对修改关闭。本质就是指当一个设计中增加新的模块时,不需要修改现有的模块。在给出一个设计时,应当首先考虑到用户需求的变化,将最有可能变化的部分设计为对扩展开放,而设计的核心部分是精心考虑后确定下来的,应该对修改关闭,即不能因为用户需求的改变而修改。
3. 多用组合少用继承原则
方法复用的两种最常用的技术就是类继承和对象组合。
3.1 继承与复用
子类通过继承父类可以复用父类的方法
优点:
1.子类在需要的时候可以重写父类的方法,易于修改或扩展那些被复用的方法
缺点:
1.无法在运行期间改变从父类继承的方法的行为
2.继承是强耦合的关系,即当父类的方法的行为改变时,必然导致子类相应方法改变
3.通过继承复用也称为“白盒”复用,这样就是说父类的内部细节对于子类而言是可见的
3.2 组合与复用
一个类将其它对象作为自己的组成部分,即Has-A,这样就可以通过委托其它对象调用其方法来达到复用的目的。
优点:
1.组合复用称为“黑盒”复用,即当前对象对所包含对象的具体细节是不可见的
2.组合是弱耦合的关系,即修改所包含的对象的类的代码,不必修改当前类的代码
3.当前对象可以在运行时动态指定所包含的对象
缺点:
1.导致系统中对象过多
2.要组合多个对象时,必须仔细地对接口进行定义
4. 高内聚-低耦合原则
高内聚:类中的方法是一组相关的行为
低耦合:尽量不要让一个类含有太多其它类的实例引用,以避免“牵一发而动全身”