本文最后更新于 2025年6月24日 下午
面向对象程序设计中的类型组织方式 在面向对象编程中,合理组织类型之间的关系是构建可维护、可扩展系统的关键。本文将探讨四种主要的类型组织方式:继承、组合、接口和代理,分析它们各自的特点、优势和适用场景。
1. 继承:建立”是什么”的关系 继承通过建立父子类关系来实现代码复用和多态性。子类继承父类的属性和方法,同时可以重写父类方法来实现特定行为。
1 2 3 4 5 6 [Animal] (父类: 抽象) | +----+----+ | | [Cat] [Dog] (子类: 具体实现)
核心特点
建立”is-a”关系(子类是父类的一种)
支持多态:通过父类引用调用子类方法
编译时确定方法签名,运行时动态绑定具体实现
代码示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public abstract class Animal { public abstract void makeSound () ; public void sleep () { System.out.println("Animal is sleeping" ); } }class Cat extends Animal { @Override public void makeSound () { System.out.println("Meow meow" ); } }class Dog extends Animal { @Override public void makeSound () { System.out.println("Woof woof" ); } }public class InheritanceDemo { public static void main (String[] args) { Animal cat = new Cat (); Animal dog = new Dog (); cat.makeSound(); dog.makeSound(); } }
优势与局限 优势:
代码复用性强
支持多态,提高代码灵活性
符合直观的分类思维
局限:
单继承限制(Java不支持多重继承)
强耦合,父类变化影响所有子类
继承层次过深时难以维护
2. 组合:建立”有什么”的关系 组合通过将其他对象作为成员变量来实现功能复用,建立”has-a”关系。这种方式提供了比继承更大的灵活性。
1 2 3 4 5 6 [Cat] [Dog] | | +------+ +------+ | Bark | | Bark | +------+ +------+
代码示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class SoundMaker { public void makeSound (String sound) { System.out.println(sound); } }class Cat { private SoundMaker soundMaker = new SoundMaker (); public void makeSound () { soundMaker.makeSound("Meow meow" ); } }class Dog { private SoundMaker soundMaker = new SoundMaker (); public void makeSound () { soundMaker.makeSound("Woof woof" ); } }
优势
避免继承的强耦合问题
支持多重”拥有”关系
运行时可以动态改变行为
更好的封装性
3. 接口:定义”能做什么”的契约 接口定义了一组行为规范,实现类必须提供这些行为的具体实现。接口关注的是对象的能力而非身份。
1 2 3 4 5 6 [Bark Interface] | +-----+-----+ | | [Cat] [Dog] (实现接口的具体类)
代码示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public interface SoundMakeable { void makeSound () ; }public interface Moveable { void move () ; }class Cat implements SoundMakeable , Moveable { @Override public void makeSound () { System.out.println("Meow meow" ); } @Override public void move () { System.out.println("Cat is walking silently" ); } }class Dog implements SoundMakeable , Moveable { @Override public void makeSound () { System.out.println("Woof woof" ); } @Override public void move () { System.out.println("Dog is running" ); } }public class InterfaceDemo { public static void main (String[] args) { SoundMakeable[] animals = {new Cat (), new Dog ()}; for (SoundMakeable animal : animals) { animal.makeSound(); } } }
优势
支持多重实现,解决单继承限制
降低耦合,面向契约编程
更好的可测试性和可扩展性
4. 代理:控制对象访问的中介 代理模式通过创建代理对象来控制对原对象的访问,可以在不改变原对象的情况下增加额外功能。
1 2 3 4 5 6 [Caller] | [Proxy] | [Real Object]
静态代理示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class AnimalKeeper { private Animal animal; public AnimalKeeper (Animal animal) { this .animal = animal; } public void makeSound () { System.out.println("Keeper: Animal is about to make sound" ); animal.makeSound(); System.out.println("Keeper: Animal finished making sound" ); } }
动态代理示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public interface Animal { void makeSound () ; }class Cat implements Animal { @Override public void makeSound () { System.out.println("Meow meow" ); } }class AnimalProxy implements InvocationHandler { private Animal target; public AnimalProxy (Animal target) { this .target = target; } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before calling " + method.getName()); Object result = method.invoke(target, args); System.out.println("After calling " + method.getName()); return result; } public Animal getProxy () { return (Animal) Proxy.newProxyInstance( target.getClass().getClassLoader(), new Class []{Animal.class}, this ); } }public class ProxyDemo { public static void main (String[] args) { Animal cat = new AnimalProxy (new Cat ()).getProxy(); cat.makeSound(); } }
代理的优势
可以在不修改原对象的情况下增加功能
支持延迟加载、访问控制、日志记录等横切关注点
动态代理提供了运行时的灵活性
5. 四种方式的比较与选择
方式
关系类型
耦合度
灵活性
适用场景
继承
is-a
强
低
明确的分类关系,稳定的层次结构
组合
has-a
弱
高
功能复用,避免继承限制
接口
can-do
很弱
很高
行为规范,多重能力描述
代理
控制访问
中等
中等
访问控制,功能增强
6. 最佳实践建议
优先使用组合而非继承 :遵循”组合优于继承”原则
多使用接口 :面向接口编程,提高代码的可扩展性
合理运用代理 :在需要控制访问或增强功能时考虑代理模式
避免深层继承 :继承层次不宜过深,一般不超过3-4层
AOP实现 动态代理的CGLIB实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class TestCGLibProxy { public void test () { Enhancer enhancer = new Enhancer (); enhancer.setSuperclass(UserService.class); enhancer.setCallback(new MyCallback (new UserService ())); Object o = enhancer.create(); } }public class MyCallback implements MethodInterceptor { Object target ; public MyCallback (Object target) { this .target = target } @Override public Object intercept (Object obj ,Method method ,Object[] ar,MethodProxy proxy) throws Throwable{ System.out.println("前置" ); proxy.invoke(target,args); System.out.println("后置" ); } return null ; }
与JDK动态代理的区别
JDK动态代理只能代理实现了接口的类 → 通过接口实现进行代理
而 CGlib 可以代理未实现任何接口的类 → 通过继承类的方式进行代理(不能将基类声明为final)
性能对比
加载阶段 JDK 动态代理只会生成一个代理类, 而GClib会生成多个相关代理类
调用阶段 method 通过反射调用, proxy 通过调用父类目标对象