Stay Hungry Stay Foolish

面向对象系统的基本知识

Posted on By Jun Xi Gu

本文是对面向对象系统的基本知识的汇总和个人理解


面向对象的基本术语

方法 是一个操作或一个请求,包含名字,参数和返回值

接口 是一系列方法的集合,对象可以拥有接口,所以接口体现了一个对象所能完成的功能

类型 是一个接口的名字,可以理解为等同于接口,类型可以有子类型和父类性,当一个类型包含另一个类型的时即为子类型

定义一个对象,提供(多个)类型里方法的实现,这是对象的一个静态写照

对象 面向对象系统里运行时的基本元素,通过各个对象之间的交互(互相发送请求消息)来实现面向系统的功能;对象之间可以相互交互,对象是一个类的实例化,可以拥有多个类型

例如:

interfaced Duck { // 类型
	void quark() // 方法
}

class ConcreteDuck implements Duck { // 类,定义了Duck类型的实现
	static void quark() {
		// real quark
	}
	
	public static void main(String[] args) {1l
		Duck d1 = new ConcreteDuck();
		Duck d2 = new ConcreteDuck();
		
		d1.quark();
		d1.quark();
	}
}

runtime

面向对象系统的核心概念

封装 把数据和对数据的操作聚合起来形成一个对象

例如:

class Duck { // 把name和quark封装成一个对象
	String name;
	
	void quark() {
		System.out.println(name + " quarking...");
	}
}

继承 包括两个含义

  • 类型继承:定义子类型,实现不同类型的替换
  • 实现继承:实现代码的复用和实现的方法重定义

类型继承举例:

interface Quarkable {
	void quark()
}

interface Duck extends { // 类型继承
}

class ConcreteQuarkable implements Quarkable {}

class ConcreteDucke implements Quarkable {}

class Demo {
	static void runDemo(Quarkable q) {
		q.quark()
	}
	
	public static void main(String[] args) {1l
		runDemo(new ConcreteQuarkable());
		runDemo(new ConcreteDucke());
	}
}

实现继承举例:

class ConcreteQuarkable {
	void quark() { }
}

class ConcreteDucke1 extends ConcreteQuarkable { } // 继承了quark的实现

class ConcreteDucke2 extends ConcreteQuarkable {
	void quark() { }  // 重定义了quark的实现
}

多态 在运行时动态的替换拥有相同类型的对象,如上类型继承举例

代码和功能的复用机制

继承 可以实现代码的复用,缺点:这是在编译时实现的代码复用,所以是一种相对运行时来说的静态复用,不够灵活;子类会破坏父类的封装性,并对父类的代码产生依赖;优点是简单粗暴

举例:

class HashSet {
	...
	void addAll(Collection c) {
		for(Object o : i)
			add(o);
	}
	
	void add(Object o) {
		...
	}
	...
}

class BraokenSubSet { // broken cause dependency
	int counter = 0;

	@Override void add(Object o) { couter++; super.add(o); }
	
	@Override void addAll(Collection c) { couter += c.size(); super.addAll(c); }
}

组合 可以实现运行时的对象功能复用,通过多态能更加灵活; 缺点是会增加对象的数量导致增加运行时的复杂性

举例:

interfaced Duck {
	void quark()
}

class Demo {
	Duck d;
	
	Demo(Duck d) { this.d = d; }
	
	void setDuck(Duck d) { this.d = d; }
	
	void quark() { d.quark(); }
	
	public static void main(String[] args) {
		Demo demo = new Demo(new Duck(){
			void quark() {
				// implement version 1
			}
		});
		demo.quark();
		
		demo.setDuck((new Duck(){
			void quark() {
				// implement version 2
			}
		});
		demo.quark();
	}
}

委托 通过组合来复用代码的功能,但又不需要继承

例如:

interface Duck { void quark(); }

class ConcreteDucke1 extends Duck { 
	void quark() { }
}

class ConcreteDucke2 extends ConcreteQuarkable {
	ConcreteDucke1 d;
	
	ConcreteDucke2(ConcreteDucke1 d) { this.d = d; }
	
	void quark() { d.quark(); }
}

参数化类型 把类型作为一种可变的参数,是的代码不依赖于类型

例如:List,Set,Map等

面向对象设计的原则

前提:视情况有取舍地使用这些原则,没有死规定

封装变化 把可变的部分封装成对象

例如:父类的两方法的实现在子类中存在着变化和重复时,应该单独封装

// Bad example
class Duck {
	void fly() { System.out.println("Fly"); }
	void quark() { System.out.println("quark"); }
}

class ToyDuck extends Duck {
	@Override void fly() {}
	@Override void quark() { System.out.println("toy quark"); }
}

class WildDuck extends Duck {
	@Override void fly() { System.out.println("Fly Height"); }
	@Override void quark() { System.out.println("quark loudly"); }
}

class CannotFlyWildDuck extends Duck {
	@Override void fly() {} // 重复的代码
	@Override void quark() { System.out.println("quark loudly"); } // 重复的代码
}

// Good Example
interface Quarkable { void quark(); }

class Normal implements Quarkable { void quark() { System.out.println("quark"); } }
class Silece implements Quarkable { void quark() {} }
class Loud implements Quarkable { void quark() { System.out.println("quark loudly"); } }
class ToySound implements Quarkable { void quark() { System.out.println("toy quark"); } }

abstract Duck {
	Quarkable q;
	Duck() { this.q = new Normal(); }
	void quark() { q.quark(); }
	
	void fly();
}

class Duck extends Duck {
	void fly() { System.out.println("Fly"); }
}

class CannotFlyWildDuck extends Duck {
	@Override void fly() {}
}

class ToyDuck extends CannotFlyWildDuck {
	ToyDuck() { this.q = new ToySound(); }
}

class WildDuck extends Duck {
	WildDuck() { this.q = new Loud(); }
	@Override void fly() { System.out.println("Fly Height"); }
}

针对接口编程而非针对实现编程 包括2个方面:

  • 客户代码使用接口编程能获得动态替换具体实现的灵活性
  • 接口的实现部分可以获得独立性和扩展性

多用组合,少用继承 对父类实现的依赖导致父类的修改影响到子类

为了交互对象之间的松耦合设计而努力 松耦合是指交互的对象之间不知道彼此的实现细节,只知道彼此拥有的接口,所以对象的具体实现的改变能互不影响

开放–关闭原则 已有代码应该对扩展开放,对修改关闭;集中精力实现最后可能改变的地方即可

依赖倒置原则 要依赖抽象的类型,不要依赖具体实现的类;编码时就是高层组件不要依赖底层组件

最少只是原则 尽量减少对象间的相互依赖,否则动一发而动全身;要遵守这个原则就应该之访问以下范围内的对象的方法:

  • 该对象本身
  • 作为参数传进来的对象
  • 此方法所创建的对象
  • 对象的组件

好莱坞原则:别调用我们,我们调用你 高层组件决定怎样使用提供给底层组件的钩子

单一责任原则 一个类应该只有一个引起变化的原因