一般而言,JAVA 语言的依赖关系体现在局部变量、方法参数以及静态方法的调用
- 善为士者不武
- 天下有道,却走马以粪,天下无道,戎马生于郊.
开-闭原则讲的是:一个软件实体应该对扩展开放,对修改关闭(Software entries should be open for extension, but closed for modification)
抽象化是关键
在java语言设计中,应该给系统定义出一个一劳永逸、不再更改的抽象设计,此设计有无穷无尽的行为在实现层被实现.
- 可以给出一个或者多个抽象JAVA类或者接口,规定出所有的具体类必须提供的方法的特征(Signature)作为系统设计的抽象层,抽象层预见了所有可能的扩展,因此在任何扩展情况下都不需要修改,从而满足了开-闭原则的第二条:对修改关闭
- 由于从抽象层导出一个或者多个新的具体类可以改变系统的行为,因此系统的设计对扩展是开放的,这就满足了开-闭原则的第二条:对扩展开放
对可变性的封装原则
- 开-闭原则如果从另外一个角度考虑,就是所谓的“对可变性的封装原则(Principle of Encapsulation of Variation)”
- 继承应当被看做是封装变化的方法,而不应当被认为是从一般性的对象生成特殊对象的方法
JAVA的接口和抽象类一样都是用来声明一个新的类型,并且作为一个类型的等级结构的起点,但是java的接口具有比抽象类更好的特征,因此应该应该优先使用java接口声明一个超类
抽象类仅提供了一个类型的部分实现。抽象类可以有实例变量以及一个或者多个构造法方法。抽象类可以同时具有抽象方法和具体方法
定义: 如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都替换成o2时,程序P的行为没有发生改变,那么类型T2是类型T1的子类型。换言之,一个软件实体如果使用的是一个基类的话,那么也一定适用其子类,而且它根本不能察觉出基类对象和子类对象的区别
里氏替换原则是继承复用的基石。只有当衍生类可以替换掉基类,软件的单位功不能受到影响时,基类才能真正被复用,而衍生类才能够在基类基础上增加新的行为
一般而言,如果有两个具体类A和B有继承关系,那么一个最简单的符合里氏替换原则的方案应该是:建立一个抽象类C,然后让类A和类B成为抽象类C的子类
里氏替换原则的理解:
- “白马,马也;乘白马,乘马也。骊马,马也;乘骊马,乘马也”
- “妹,美人也,爱妹,非爱美人也”
简单工厂模式的例子
package simpleFactory;
public interface Fruit {
void grow() ;
void harvest() ;
void plant() ;
}
package simpleFactory;
public class Apple implements Fruit {
private int treeAge ;
@Override
public void grow() {
log("Apple is growing...") ;
}
@Override
public void harvest() {
log("Apple has been harvested") ;
}
@Override
public void plant() {
log("Apple has been planted") ;
}
public static void log(String str){
System.out.println(str) ;
}
public int getTreeAge(){
return treeAge ;
}
public void setTreeAge(int treeAge){
this.treeAge = treeAge ;
}
}
package simpleFactory;
public class Grape implements Fruit{
private boolean seedless ;
@Override
public void grow() {
log("Grape is growing...") ;
}
@Override
public void harvest() {
log("Grape has been harvested") ;
}
@Override
public void plant() {
log("Grape has been planted") ;
}
public void log(String str){
System.out.println(str) ;
}
public boolean getSeedless(){
return seedless ;
}
public void setSeedless(boolean seedless){
this.seedless = seedless ;
}
}
package simpleFactory;
public class Strawberry implements Fruit {
@Override
public void grow() {
log("Strawberry is growing...") ;
}
@Override
public void harvest() {
log("Strawberry has been harvested") ;
}
@Override
public void plant() {
log("Strawberry has been planted") ;
}
public void log(String str){
System.out.println(str) ;
}
}
package simpleFactory;
public class FruitGardener {
public static Fruit factory(String which)throws BadFruitException{
if(which.equalsIgnoreCase("apple")){
return new Apple() ;
}else if(which.equalsIgnoreCase("grape")){
return new Grape() ;
}else if(which.equalsIgnoreCase("strawberry")){
return new Strawberry() ;
}else{
throw new BadFruitException("Bad fruit request") ;
}
}
}
package simpleFactory;
public class BadFruitException extends Exception {
private static final long serialVersionUID = 1L;
public BadFruitException(String str){
super(str) ;
}
}
package simpleFactory;
public class Main {
public static void main(String args[]){
try{
Fruit apple = FruitGardener.factory("Apple") ;
Fruit strawberry = FruitGardener.factory("strawberry") ;
Fruit grape = FruitGardener.factory("grape") ;
//Fruit f = FruitGardener.factory("a") ;
apple.harvest() ;
apple.plant() ;
apple.grow() ;
strawberry.grow() ;
grape.grow() ;
}catch(BadFruitException e){
System.out.println(e) ;
}
}
}
简单的工厂模式就是一个工厂类根据传入参量决定创建出哪一种产品类的实例。
工厂类(Creator)角色
担任这个角色的是工厂方法模式的核心。工厂类在客户端的直接调用下创建产品对象,它往往由一个具体java类实现
抽象产品(Product)角色
担任这个角色的类是由工厂方法模式所创建的对象的父类,或它们共同拥有的接口。抽象产品的角色可以用一个java接口或者java抽象类实现
具体产品(Concrete Product)
工厂方法模式所创建的任何对象都是这个角色的实例,具体产品由一个java类实现
package pattern;
public class Creator{
public static Product factory(){
return new ConcreteProduct() ;
}
}
package pattern;
public interface Product{
}
package pattern;
public class ConcreteProtuct(){
public ConcreteProduct(){
}
}
最经典的应用是 java.text.DateFormat 类
如果抽象产品角色应经被忽略,那么具体产品角色可以有自己的工厂
package pattern;
public class ConcreteProduct{
public ConcreteProduct(){
}
public static ConcretePorduct factory(){
return new ConcteteProduct() ;
}
}
这里使用静态工厂的方法是为了将具体子类实例化的工作隐藏起来,从而客户端不必考虑如何将具体子类实例化,因为抽象类DateFormat会提供合适的具体子类的实例
DateFormat
是抽象类,SimpleDateFormat
是DateFormat
的子类,静态方法getDateInstance()
完全可以返回SimpleDateFormat
的实例,并且将之声明为DateFormat
类型,这是最纯正的对象的多态性的应用
这也是java针对抽象编程的具体体现