面向对象的设计原则(1)
一、概况
1、面向对象的关键字
- 类、对象、引用、属性、方法
- 接口、抽象类
- 继承、组合
- 私有、包、共有
- ……
2、面向对象编程
- 一切皆是对象
- 程序是对象的集合,它们通过发送消息告诉彼此该做什么
- 每个对象都有自己的属性(存储)
- 每个对象都拥有类型
- 某一特性类型的所有对象都可以接受同样的消息
总结起来:状态、行为、标示
状态(属性)、行为(方法)、标示(说明了我们不同的类型区别)
3、面向对象的原则
- 高内聚、低耦合 (高内聚是指:一个组件内部是一个非常紧密的集合)
- 将变化与不变分开
- 依赖抽象,而不是依赖具体
- 多用组合、少用继承
- 开放-关闭原则
- ……
二、面向对象的设计实例
1、New的不足
1 package objectoriented.coupling1; 2 3 public class Example { 4 public static void main(String[] args) { 5 Television tv = new Television(); 6 tv.play(); 7 } 8 } 9 10 class Television { 11 Panel panel = new LcdPanel(); 12 13 void play() { 14 panel.display(); 15 } 16 } 17 18 19 interface Panel { 20 abstract public void display(); //接口定义的方法默认是public的 21 } 22 23 class LcdPanel implements Panel { 24 int size = 32; 25 26 LcdPanel() { 27 } 28 29 LcdPanel(int size) { 30 this.size = size; 31 } 32 33 public void display() { 34 System.out.println("尺寸=" + size + ",现在开始播放..."); 35 } 36 }
- (1)产生直接依赖
- (2)只能通过参数列表区分对象的创建 —— 传入一个参数,不好理解
- (3)完全确定了对象的具体类型 ——
电视机这个类 写死了具体实现,以后想换成其它面板不好维护private Panel panel = new LcdPanel();
- (4)必定产生新的对象——比如单例模式,就并不需要每次都创建新的对象
改进1——静态工厂
1 package objectoriented.coupling2; 2 3 4 public class Example { 5 public static void main(String[] args) { 6 Television tv = new Television(); 7 tv.play(); 8 } 9 } 10 11 class Television { 12 //Panel panel = new LcdPanel(); 13 private Panel panel = PanelFactory.createLcdPanel(); 14 15 void play() { 16 panel.display(); 17 } 18 } 19 20 //任何问题都可以增加一个中间层来解决; 21 //好处,电视剧要变化面板时,只需要变化面板工厂类 22 class PanelFactory { 23 public static Panel createLcdPanel() { //Panel类型,但返回的是子类对象 24 return new LcdPanel(); 25 } 26 27 public static Panel createCrtPanel() { 28 return new CrtPanel(); 29 } 30 } 31 32 33 interface Panel { 34 abstract public void display(); 35 } 36 37 class LcdPanel implements Panel { 38 int size = 32; 39 40 LcdPanel() { 41 } 42 43 LcdPanel(int size) { 44 this.size = size; 45 } 46 47 public void display() { 48 System.out.println("尺寸=" + size + ",LcdPanel现在开始播放..."); 49 } 50 } 51 52 class CrtPanel implements Panel { 53 int size = 32; 54 55 CrtPanel() { 56 } 57 58 CrtPanel(int size) { 59 this.size = size; 60 } 61 62 public void display() { 63 System.out.println("尺寸=" + size + ",CrtPanel现在开始播放..."); 64 } 65 }
- (1)将变化控制在较小的范围——具体的依赖转移到了工厂方法
- (2)更有意义的名字
- (3)可以返回任何子类型的对象
- (4)可以重用对象——可以重用时,在静态工厂里面用一个属性缓存起来,下次调用直接返回
静态工厂的不足:
- (1)创建对象的方法与其它静态方法没有区别 ??? (2)名门惯例——valueOf()、getInstance()、newInstance()、getXxx()、newXxx(),
从静态方法看不出来“这是一个创建对象的方法”,只有名字取得比较像时才可以看出来
改进2——使用环境变量
1 package objectoriented.coupling3; 2 3 4 public class Example { 5 public static void main(String[] args) { 6 // System.setProperty("panel.impl.class", "objectoriented.coupling3.CrtPanel"); 7 // 一般通过参数,让Java程序运行时得到 8 System.setProperty(args[0], args[1]); 9 10 Television tv = new Television(); 11 tv.play(); 12 } 13 } 14 15 class Television { 16 //Panel panel = new LcdPanel(); 17 private Panel panel = PanelFactory.createPanel(); 18 19 void play() { 20 panel.display(); 21 } 22 } 23 24 //任何问题都可以增加一个中间层来解决; 25 //好处,电视剧要变化面板时,只需要变化面板工厂类 26 class PanelFactory { 27 private static final String DEFAULT_PANEL = "objectoriented.coupling3.LcdPanel"; 28 29 public static Panel createPanel() { 30 String panel = System.getProperty("panel.impl.class"); //获取系统变量 31 if (panel == null) { 32 panel = DEFAULT_PANEL; 33 } 34 try { 35 Class<?> panelClass = PanelFactory.class.getClassLoader() 36 .loadClass(panel); //通过参数配置不同,使得产生的实际面板对象不同 37 return (Panel)panelClass.newInstance(); 38 } catch (Exception e) { 39 throw new RuntimeException(e); 40 } 41 } 42 } 43 44 45 interface Panel { 46 abstract public void display(); 47 } 48 49 class LcdPanel implements Panel { 50 int size = 32; 51 52 LcdPanel() { 53 } 54 55 LcdPanel(int size) { 56 this.size = size; 57 } 58 59 public void display() { 60 System.out.println("尺寸=" + size + ",LcdPanel现在开始播放..."); 61 } 62 } 63 64 class CrtPanel implements Panel { 65 int size = 32; 66 67 CrtPanel() { 68 } 69 70 CrtPanel(int size) { 71 this.size = size; 72 } 73 74 public void display() { 75 System.out.println("尺寸=" + size + ",CrtPanel现在开始播放..."); 76 } 77 }
设置main方法的参数:
原理:利用环境变量来实现,环境变量(全局类名)调用System.setProperty(key, value),中间处理层再System.getProperty(key)取出全局类名,并通过反射加载类,把实例化的对象返回
使用环境变量的优势:可以灵活的通过启动配置改变接口的实现
不足:需要使用反射,即反射的不足
说明:
39 throw new RuntimeException(e);
/*
这里如果throw new Exception(e); 该类异常属于必须要处理的异常,必须交给上一级处理,
即static Panel createPanel() throws Exception,同时Panel panel = PanelFactory1.createPanel()会报如下错误
* Default constructor cannot handle exception type Exception thrown by implicit super constructor. Must define an
explicit constructor
——如果改为throw new RuntimeException(e),则本方法不需要throws异常,因为RuntimeException会交给JVM处理,直接终止程序
*/
改进3——全局注册表
1 package objectoriented.coupling4; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 6 7 public class Example { 8 public static void main(String[] args) { 9 GlobalRegistry.registerServices("panel", new LcdPanel()); //业务调用前,先注册 JNDI???最后2min 10 11 Television tv = new Television(); 12 tv.play(); 13 } 14 } 15 16 17 class Television { 18 private Panel panel = (Panel)GlobalRegistry.getService("panel"); //通过名字,取出来(即使是在别的地方注册的) 19 20 void play() { 21 panel.display(); 22 } 23 } 24 25 class GlobalRegistry { 26 private static final Map<String, Object> services = new HashMap<String, Object>(); 27 28 public static void registerServices(String serviceName, Object service) { 29 services.put(serviceName, service); //如果services非静态,则会报Cannot make a static reference to the non-static field services 30 } 31 32 public static Object getService(String serviceName) { 33 return services.get(serviceName); 34 } 35 } 36 37 38 interface Panel { 39 abstract public void display(); 40 } 41 42 class LcdPanel implements Panel { 43 int size = 32; 44 45 LcdPanel() { 46 } 47 48 LcdPanel(int size) { 49 this.size = size; 50 } 51 52 public void display() { 53 System.out.println("尺寸=" + size + ",LcdPanel现在开始播放..."); 54 } 55 } 56 57 class CrtPanel implements Panel { 58 int size = 32; 59 60 CrtPanel() { 61 } 62 63 CrtPanel(int size) { 64 this.size = size; 65 } 66 67 public void display() { 68 System.out.println("尺寸=" + size + ",CrtPanel现在开始播放..."); 69 } 70 }
- 服务提供者和消费者通过注册表关联
- 可以由注册表负责对象的创建
- J2EE大量使用此方式