一、面向抽象原则
1、抽象类和接口
(1)抽象(abstract)类具有以下特点:
A、抽象类中可以有抽象方法,也可以没有抽象方法
B、抽象类不能用new运算符创建对象
C、如果一个非抽象类是某个抽象类的子类,那么它必须重新父类的abstract方法,即在子类中将abstract方法重新声明,但必须去掉abstract修饰符,同时要保证声明的方法名、返回值类型、参数个数和类型必须与父类的abstract方法完全相同
D、作为向上转型对象。尽管抽象类不能用new运算符创建对象,但非abstract子类必须要重写全部abstract方法,这样一来,就可以让抽象类声明的对象成为其子类对象的上转型对象,并调用子类重写的方法。
例如:
public abstract class A {
public abstract int add(int x,int y);
}
public class B extends A{
@Override
public int add(int x, int y) {
return x+y;
}
}
public class Application {
public static void main(String[] args) {
A a;
a = new B();
int m = a.add(3,2); //a是B类对象的上转型对象
System.out.println(m);
}
}
(2)接口(interface)具有以下特点:
A、接口中只可以有public权限的abstract方法,不能有非abstract方法。
B、接口由类去实现,即一个类如果实现一个接口,那么它必须重写接口中的abstract方法,重写时必须去掉abstract修饰符,同时要保证声明的方法名、返回值类型、参数个数和接口中的方法完全相同。
C、接口回调,是指可以把实现接口的类的对象的引用赋给该接口声明的接口变量中,那么该接口变量就可以调用被类实现的接口中的方法,当接口变量调用被类实现的接口中的方法时,就是通知相应的对象调用接口的方法。
例如:
public interface Com {
public abstract int sub(int x,int y);
}
public class ComImp implements Com{
@Override
public int sub(int x, int y) {
return x-y;
}
}
public class Application {
public static void main(String[] args) {
Com com;
com = new ComImp();//com是接口变量存放ComImp类的对象
int m = com.sub(8,2); //com回调ComImp类实现的接口方法
System.out.println(m);
}
}
2、面向抽象
所谓面向抽象编程,是指当设计一个类时,不让该类面向具体的类,而是面向抽象类或接口,即所设计类中的重要数据是抽象类或接口声明的变量,而不是具体声明的变量。
例如,有一个Circle类,该类的对象调用getArea()方法可以计算圆的面积,代码如下:
public class Circle {
double r;
Circle(double r){
this.r = r;
}
public double getArea(){
return (3.14*r*r);
}
}
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类不能应对用户的需求变化)
采用面向抽象编程重新设计Pillar类,编写一个抽象类Geometry(或接口),该类(或接口)中定义了一个抽象方法getArea()
public abstract class Geometry {
public abstract double getArea();
}
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;
}
}
public class Circle extends Geometry{
double r;
Circle(double r){
this.r = r;
}
@Override
public double getArea() {
return (3.14*r*r);
}
}
public class Rectangle extends Geometry{
double a,b;
public Rectangle(double a, double b) {
this.a = a;
this.b = b;
}
@Override
public double getArea() {
return a*b;
}
}
public class Application {
public static void main(String[] args) {
Pillar pillar;
Geometry bottom;
bottom = new Rectangle(12,22);
pillar = new Pillar(bottom,58);
System.out.println("矩形底的柱体的体积="+pillar.getVolume());
bottom = new Circle(10);
pillar = new Pillar(bottom,58);
System.out.println("圆形底的柱体的体积="+pillar.getVolume());
}
}
通过面向抽象来设计Pillar类,使得该Pillar类不再依赖具体的类,因此每到系统增加新的Geometry的子类时,比如增加一个Triangle子类,那么不需要修改Pillar类的任何代码,就可以使Pillar创建出具有三角形底的柱体。
二、开-闭原则
所谓“开-闭原则(Open-Closed Principle)”就是让设计对扩展开放,对修改关闭。本质是指当一个设计中增加新的模块时,不需要修改现有的模块。在给出一个设计时,应当首先考虑到用户需求的变化,将应对用户变化的部分设计为扩展开放的,而设计的核心部分经过精心考虑后确定下来的基本结构应当对修改关闭,即不能因为用户的需求变化而再发生变化。
三、多用组合少用继承的原则
1、继承与复用
(1)通过继承来复用父类的方法的优点是:
子类可以重写父类的方法,即易于修改或扩展那些被复用的方法
(2)通过继承来复用父类的方法的确定是:
A、子类从父类继承的方法在编译时就确定下来了,所以无法在运行期间改变从父类继承的方法的行为
B、子类和父类的关系是强耦合关系,即父类的方法的行为改变时,必然导致子类发生变化
C、通过继承进行复用也称“白盒”复用,父类的内部细节对于子类而言是可见的
2、组合与复用
一个类的成员变量可以是Java允许的任何数据类型,因此,一个类可以把对象作为自己的成员变量。如果用这样的类创建对象,那么该对象是由几个对象组合而成。
如果一个对象a组合了对象b,那么对象a就可以委托对象b调用其方法,即对象a以组合的方式复用对象b的方法。
(1)通过组合对象来复用方法的优点是:
A、通过组合对象来复用方法也称“黑盒”复用,因为当前对象只能委托所包含的对象调用其方法,这样一来,当前对象所包含对象的方法的细节对当前对象来说是不可见的
B、当前对象与所包含的对象属于弱耦合关系
C、当前对象可以在运行时动态指定所包含的对象
(2)通过组合对象来复用方法的确定是:
A、容易导致系统中的对象过多
B、为了能组合多个对象,必须仔细地对接口进行定义
四、高内聚-低耦合原则
高内聚便于类的维护,低内聚不利于类的维护
低耦合就是尽量不要让一个类含有其他类的实例引用,以避免修改系统的其中一部分会影响到其他部分