前言
编程是一门技术,更是一门艺术。
如果想成为一名更优秀的软件设计师,了解优秀软件设计的演变过程比学习优秀设计本身更有价值,因为设计的演变过程中蕴藏着大智慧。
学习设计模式,重要的不是你将来会不会用得到这些模式,而是通过这些模式让你找到“封装变化”,“对象间松散耦合”,“针对接口编程”的感觉,从而设计出易维护,易扩展,易复用,灵活性好的程序。
成为诗人后可能不需要刻意地按照某种模式去创作,但成为诗人前他们一定是认真地研究过成百上千的唐诗宋词,古今名句。
GOF的《设计模式》:世界顶级足球射门锦集。
《重构》《敏捷软件开发》《设计模式解析》:一场场最精彩的足球比赛。
球迷(软件使用者)——>足球运动员(软件设计编程者)——>球星(软件架构师)
UML类图
类图:
类图分三层。
第一层显示类的名称,如果是抽象类,用斜体表示。
第二层是类的特性,通常是字段和属性。
第三层是类的操作,通常是方法和行为。
注意:'+'表示public,'-'表示private,'#'表示protected。
接口图:
与类图的区别主要是顶端有<
第一行:接口名称。
第二行:接口方法。
接口还有另外一种表示方法,俗称棒棒糖表示法。
类与类,类与接口的关系:
继承关系:空心三角+实线来表示。
实现接口:空心三角+虚线表示。
关联关系:实线箭头表示。
聚合关系:用空心菱形+实线箭头表示。
大雁和雁群。聚合表示一种弱的'拥有'关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分。
合成(组合)关系:实心菱形+实线箭头表示。
鸟和翅膀。合成(组合)关系是一种强的'拥有'关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。
依赖关系:虚线箭头表示。
简单工厂模式
定义:简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,是由一个工厂对象决定创建出哪一种产品类的实例。但不属于23种GOF设计模式之一。
实现方法和角色:
(1)实现方式:简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。
(2)角色:
工厂(Creator)角色
抽象产品(Product)角色
具体产品(Concrete Product)角色
优缺点:
(1)优点:在于工厂类中包含了必要的逻辑,根据客户需要的条件动态实例化相关的类,对客户端来说,去掉了与具体产品的依赖。
(2)缺点:工厂类集中了所有实例的创建逻辑,当增加新的产品时,会违反开放-封闭原则。
简单工厂模式结构图:
//抽象产品
public abstract class Computer {
public abstract void start();
}
//具体产品
public class LenovoComputer extends Computer {
public void start() {
System.out.println("联想电脑启动");
}
}
public class HpComputer extends Computer {
public void start() {
System.out.println("惠普电脑启动");
}
}
public class AsusComputer extends Computer {
public void start() {
System.out.println("华硕电脑启动");
}
}
//工厂类
public class ComputerFactory {
public static Computer createComputer(String type) {
Computer computer = null;
if("lenovo".equals(type)){
computer = LenovoComputer();
} else if("hp".equals(type)){
computer = HpComputer();
} else if("asus".equals(type)){
computer = AsusComputer();
}
return computer;
}
}
//客户端调用
public class Client {
public static void main(String[] args) {
ComputerFactory.createComputer("lenovo").start();
}
}
面试题:请用C++,Java,C#,或VB.NET任意一种面向对象语言实现一个计算器控制台程序,要求输入两个数和运算符号,得到结果。
活字印刷
- 易维护:要改,只需要改要改之字。
- 可复用:每个字在印刷中可重复使用。
- 易扩展:要加字,只需另刻字加入。
- 灵活性好:字可任意排序。
面向对象的分析设计编程思想:考虑通过封装,继承,多态把程序的耦合度降低,用设计模式使得程序更加灵活,容易修改,并且易于复用。
初级程序员的工作就是:Ctrl+C和Ctrl+V。
编程有一个原则:用尽可能的办法避免重复。代码重复到一定程度,维护就是一场灾难。
复制粘贴是最容易的编程,也是最没有价值的编程。
封装:
业务封装,将界面逻辑和业务逻辑分开。降低耦合度,达到易维护。
继承:
将加减乘除等运算分离。修改或增加新的运算方法,不影响原有功能,降低风险。
多态+简单工厂模式:
实例化哪个运算对象?应该用一个单独的类来做这个创建实例的过程,这就是工厂。
public abstract class Operate {
public double numberA;
public double numberB;
public abstract double getResult();
//TODO get set 方法
}
public Class OperateAdd extends Operate {
public double getResult(){
return numberA + numberB;
}
}
public Class OperateSub extends Operate {
public double getResult(){
return numberA - numberB;
}
}
public Class OperateMul extends Operate {
public double getResult(){
return numberA * numberB;
}
}
public Class OperateDiv extends Operate {
public double getResult() {
return numberA / numberB;
}
}
public Class OperateFactory {
public static Operate createOperate(String oper){
Operate operate = null;
if("+".equal(oper)){
operate = new OperateAdd();
} else if("-".equal(oper)){
operate = new OperateSub();
} else if("*".equal(oper)){
operate = new OperateMul();
} else if("/".equal(oper)){
operate = new OperateDiv();
}
return operate;
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个数字A:");
double numberA = scanner.nextDouble();
System.out.println("请输入一个运算符(+,-,*,/):");
String oper = scanner.next();
System.out.println("请输入一个数字B:");
double numberB = scanner.nextDouble();
Operate operate = OperateFactory.createOperate(oper);
operate.setNumberA(numberA);
operate.setNumberB(numberB);
System.out.println(operate.getResult());
}
使用场景:
工厂类负责创建的对象比较少。
客户只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心。
优点:
使用户根据参数获得相应的类实例,避免了直接实例化,降低了耦合性。
缺点:
增加新类型,需要修改工厂,违背了开放封闭原则。
简单工厂需要知道所有要生成的类型,当子类过多或者子类层次过多时不适合使用。
不能只满足于写完代码运行结果正确就完事,时常考虑如何让代码更加简练,更加容易维护,容易扩展和复用,只有这样才可以真正得到提高。
工厂方法模式
定义:定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类。
依赖倒置原则
工厂方法模式:克服了简单工厂违背"开发-封闭原则"的缺点,又保持了封装对象创建过程的优点。
工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。缺点:每加一个产品,就需要加一个产品工厂类,增加了额外的开发量。
工厂方法模式(Factory Method)结构图:
//抽象产品
public abstract class Computer {
public abstract void start();
}
//具体产品
public LenovoComputer extends Computer {
public void start() {
System.out.println("联想电脑启动");
}
}
public HpComputer extends Computer {
public void start() {
System.out.println("惠普电脑启动");
}
}
public AsusComputer extends Computer {
public void start() {
System.out.println("华硕电脑启动");
}
}
//抽象工厂
public abstract class ComputerFactory {
public abstract T createComputer(Class clz);
}
//具体工厂
public class ConcreteFactory extends ComputerFactory {
public T createComputer(Class clz) {
Computer computer = null;
String className =clz.getName();
try{
computer = (Computer)Class.forName(className).netInstance();
}catch(Exception e) {
e.printStackTrace();
}
return (T)computer;
}
}
//客户端调用
public class Client {
public static void main(String[] args) {
ComputerFactory computerFactory = new ConcreteFactory();
Computer computer = null;
computer = computerFactory.createComputer(LenovoComputer.class);
computer.start();
computer = computerFactory.createComputer(HpComputer.class);
computer.start();
computer = computerFactory.createComputer(AsusComputer.class);
computer.start();
}
}
这样整个工厂和产品体系其实都没有修改的变化,而只是扩展的变化,这就完全符合了开放-封闭原则。
工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现运算类,工厂方法把简单工厂的内部逻辑判断转移到了客户端代码来进行。
抽象工厂模式
定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。
当需要创建的产品有多个产品线(产品族)时,使用抽象工厂模式是比较好的选择。
那什么是多个产品线?联想和惠普的电脑,电脑也有多个产品线:台式机、笔记本和平板。联想和惠普都在生产这些不同产品线上的电脑,使用工厂方法模式已经满足不了需求,可用抽象工厂模式来解决。
抽象工厂(Abstract Factory)模式结构图:
//抽象产品
public abstract class DesktopComputer {
public abstract void start();
}
public abstract class NotebookComputer {
public abstract void start();
}
//具体产品
public LenovoDesktopComputer extends DesktopComputer {
public void start() {
System.out.println("联想台式机启动");
}
}
public HpDesktopComputer extends DesktopComputer {
public void start(){
System.out.println("惠普台式机启动");
}
}
public LenovoNotebookComputer extends NotebookComputer {
public void start() {
System.out.println("联想笔记本启动");
}
}
public HpNotebookComputer extends NotebookComputer {
public void start() {
System.out.println("惠普笔记本启动");
}
}
//抽象工厂
public abstract class ComputerFactory {
public abstract DesktopComputer createDesktopComputer();
public abstract NotebookComputer createNotebookComputer();
}
//具体工厂
public class LenovoFactory extends ComputerFactory {
public DesktopComputer createDesktopComputer() {
return new LenovoDesktopComputer();
}
public NotebookComputer createNotebookComputer() {
return new LenovoNotebookComputer();
}
}
public class HpFactory extends ComputerFactory {
public DesktopComputer createDesktopComputer() {
return new HpDesktopComputer();
}
public NotebookComputer createNotebookComputer() {
return new HpNoteBookComputer();
}
}
//客户端调用
public class Client {
public static void main(String[] args) {
ComputerFactory lenovoFactory = new LenovoFactory();
lenovoFactory.createDesktopComputer().start();
lenovoFactory.createNotebookComputer().start();
ComputerFactory hpFactory = new HpFactory();
hpFactory.createDesktopComputer().start();
hpFactory.createNotebookComputer().start();
}
}
抽象工厂模式的优缺点:
优点:
具体类的创建实例过程与客户端分离,客户端通过工厂的抽象接口操纵实例,客户端并不知道具体的实现是谁。
缺点:
如果增加新的产品族则也需要修改抽象工厂和所有的具体工厂。
策略设计模式
策略设计模式(Strategy):它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
面试题:请用任意面向对象语言,设计实现一个商场收银功能,需包含:打折(9折,8折,五折),返现(满300返100,满100返50),及正常价格收费。
面向对象编程,并不是类越多越好,类的划分是为了封装,但分类的基础是抽象,具有相同属性和功能的对象的抽象集合才是类。
商场促销,无论是打折还是返现,其实都是一些算法,但算法本身只是一种策略,最重要的是这些算法随时都可能互相替换,这就是变化点,封装变化点是我们面向对象的一种很重要的思维方式。
- 现金收费抽象类。
- 正常收费子类。
- 打折收费子类。
- 返现收费子类。
//现金收费抽象类
public abstract class CashSuper {
public abstarct double acceptCash(double money);
}
//正常收费子类
public class CashNormal extends CashSuper {
public double acceptCash(double money) {
return money;
}
}
//打折收费子类
public class CashRebate extends CashSuper {
private double rebate;
public CashRebate(double rebate){
this.rebate = rebate;
}
public double acceptyCash(double money) {
return money * rebate;
}
}
//返现收费子类
public class CashReturn extends CashSuper {
private double moneyCondition;
private double moneyReturn;
public CashReturn(double moneyCondition,double moneyReturn) {
this.moneyCondition = moneyCondition;
this.moneyReturn = moneyReturn;
}
public double acceptCash(double money) {
double resule = money;
if(money >= moneyCondition){
result = money - Math.floor(money / moneyCondition) * moneyReturn ;
return result;
}
}
}
//上下文,维护一个对Strategy对象的引用
public class CashContext {
private CashSuper cashSuper;
public CashContext (CashSuper cashSuper) {
this.cashSuper = cashSuper;
}
public double getResult(double money){
return cashSuper.acceptCash(money);
}
}
策略模式和简单工厂模式结合,将客户端条件判断转移到CashContext中。
客户端调用略
总结:
当不同的行为堆砌在一个类中,就很难避免使用条件语句来选择合适的行为。将这些行为封装在一个个独立的Strategy类中,可以在使用这些行为的类中消除条件语句。
策略模式封装了变化。
策略模式就是用来封装算法的,只要在分析的过程中听到需要在不同的时间应用不同的业务规则,就可以考虑是使用策略模式。
消除switch或条件判断:反射技术。
代理设计模式
代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问。
为什么要有代理模式?
开闭原则,对扩展开放,对修改关闭。
为什么有静态代理和动态代理?
最大的缺点:接口变化了,除了实现类需要改变,代理也需要修改。实际上我们是不想让代理也需要修改的。所以出现了动态代理。
jdk 动态代理的缺点?
必须要实现接口。
jdk动态代理为什么必须要实现接口?
因为他默认继承的proxy类,java是单继承,所以必须要实现接口。
spring aop 最核心的思想:动态代理。
什么样的场景下用cglib?
看代理的对象有没有接口,没有接口的用cglib。
代理模式使用场景
无法或者不想直接访问某个对象时可以通过一个代理对象来间接的访问。
//静态代理
//接口
public interface IuserDao {
void save();
}
//实现类
public class UserDaoImpl implements IuserDao {
public void save(){
System.out.println("保存用户");
}
}
//代理类
public class UserDaoProxy implements IUserDao {
private IUserDao userDao;
public UserDaoProxy(IUserDao userDao){
this.userDao = userDao;
}
public void save(){
System.out.println("操作前");
userDao.svae();
System.out.println("操作后");
}
}
//测试
public static void main(String[] args) {
//静态代理
IUserDao userDao = new UserDaoProxy(new UserDaoImpl());
userDao.save();
//动态代理
IUserDao userDao = (IUserDao)Proxy.newProxyInstance(IUserDao.class.getClassLoader,new Class[]{IuserDao.class},new InvocationHandler(){
public Object invoke(Object proxy,Method method,Object[] args){
System.out.println("操作前");
Object object = method.invoke(new UserDaoImpl(),args);
System.out.println("操作后");
return object ;
}
});
}
单例设计模式
定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
- 饿汉式(线程安全)
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return instance;
}
}
这种方式在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快。
基于类加载机制避免了多线程的同步问题。但是也不能确定有其他方式导致类装载。
- 懒汉式(线程不安全)
public class Singleton(){
private static Singleton instance = null;
private Singleton(){
}
public static Singleton getInstance(){
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
初始化时节约资源,第一次加载是需要实例化反映稍慢一些,多线程不能正常工作。
- 懒汉式(线程安全)
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
每次调用getInstance方法时都需要同步,造成不必要的同步开销,大部分时候是用不到同步的,所以不建议使用这种模式。
- 双重检查枷锁(DCL)
public class Singleton {
private volatile static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null) {
synchroinzed(Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
这种写法在getSingleton方法中对singleton进行了两次判空,第一次是为了不必要的同步,第二次是在singleton等于null的情况下才创建实例。
DCL虽然在一定程度解决了资源的消耗和多余的同步,线程安全等问题,但是他还是在某些情况会出现失效的问题,也就是DCL失效,在《java并发编程实践》一书建议用静态内部类单例模式来替代DCL。
- 静态内部类
public class Singleton {
private Singleton(){}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
}
第一次加载Singleton类时并不会初始化instance,只有第一次调用getInstance方法时,虚拟机加载SingletonHolder并初始化instance,这样不仅能确保线程安全也能保证Singleton类的唯一性,所以推荐使用静态内部类单例模式。
- 枚举
public enum Singleton {
INSTANCE;
}
总结:
枚举式是最简单最优秀的单例写法,可以防止反射工具(《如何防止单例模式被Java反射攻击》)和序列化破坏。《Effective Java》的作者Joshua Bloch推荐使用这种写法,只是用的人较少,没有普遍性,建议编程时采用静态内部类(不能防止反射和序列化破坏)。
模板方法模式
定义:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
当我们要完成一系列步骤,但其个别步骤在更详细的层次上的实现可能不同时,我们通常考虑使用模板方法模式来处理。
模板方法模式(TemplateMethod)结构图:
原型设计模式
定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节。
一般在初始化信息不发生变化的情况下,克隆是最好的办法,这既隐藏了创建对象的细节,又对性能是大大的提高。
原型模式(Prototype)结构图:
Client:客户端角色。
Prototype:抽象原型角色,抽象类或者接口,用来声明clone方法。
ConcretePrototype:具体的原型类,是客户端角色使用的对象,即被复制的对象。
注意:Prototype通常是不用自己定义的,因为拷贝这个操作十分常用,Java中就提供了Cloneable接口来支持拷贝操作,它就是原型模式中的Prototype。当然,原型模式也未必非得去实现Cloneable接口,也有其他的实现方式。
Cloneable接口是一个标识接口,表示这个对象是可拷贝的,只要重写clone方法就可以实现拷贝。clone方法不是在Cloneable接口中定义的(Cloneable接口中没有定义任何方法),而是在Object中定义的。
//浅复制
public class BusinessCard implements Cloneable {
private String name;
private String company;
//TODO get set方法
public BusinessCard(){
System.out.println("调用构造方法");
}
public Object clone() throws CloneNotSupportException{
return super.clone();
}
public void show() {
System.out.println("name:"+name+",compnay:"+company);
}
}
//客户端
public class Clinet {
public static void main(String[] args) {
BusinessCard businessCard = new BusinessCard();
businessCard.setName("张三");
businessCard.setCompany("百度");
BusinessCard cloneCard1 = businessCard.clone();
cloneCard1.setName("李四");
cloneCard1.setCompany("阿里巴巴");
BusinessCard cloneCard2 = businessCard.clone();
cloneCard2.setName("王五");
cloneCard2.setCompany("腾讯");
businessCard.show();
cloneCard1.show();
cloneCard2.show();
}
}
//对象类型
public class Company {
private String name;
private String address;
//TODO get set 方法
}
public class BusinessCard implements Cloneable {
private String name;
private Company company = new Company();
//TODO get set 方法
public BusinessCard(){
System.out.println("初始化构造方法");
}
public void setCompany(String name,String address){
this.company.setName(name);
this.company.setAddress(address);
}
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public void show(){ System.out.println("name:"+name+",compnayName:"+company.getName()+",compnayAddress:"+company.getAddress());
}
}
//客户端
public class Clinet {
public static void main(String[] args) {
BusinessCard businessCard = new BusinessCard();
businessCard.setName("张三");
businessCard.setCompany("百度", "西二旗");
BusinessCard cloneCard1 = businessCard.clone();
cloneCard1.setName("李四");
cloneCard1.setCompany("联想", "中关村");
BusinessCard cloneCard2 = businessCard.clone();
cloneCard2.setName("王五");
cloneCard2.setCompany("腾讯", "望京");
businessCard.show();
cloneCard1.show();
cloneCard2.show();
}
}
//输出结果company:全为 腾讯 望京
String是一种拥有值类型特定的特殊引用类型。
这是因为Object类提供的clone方法,不会拷贝对象中的内部数组和引用对象,导致它们仍旧指向原来对象的内部元素地址,这种拷贝叫做浅拷贝。
实现深拷贝:
//修改Company实现Cloneable接口,实现clone方法
public class Company implements Cloneable {
private String name;
private String address;
//TODO get set 方法
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
//修改BusinessCard的clone方法:
protected Object clone() throws CloneNotSupportedException {
BusinessCard businessCard = null;
businessCard = (BusinessCard)super.clone();
businessCard.company = (Company)company.clone();
return businessCard;
}
原型设计模式的使用场景:
- 如果类的初始化需要耗费较多的资源,那么可以通过原型拷贝避免这些消耗。
- 通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
- 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以拷贝多个对象供调用者使用,即保护性拷贝。
优点:
原型模式是在内存中二进制流的拷贝,要比new一个对象的性能要好,特别是需要产生大量对象时
缺点:
直接在内存中拷贝,构造函数是不会执行的,这样就减少了约束,这既是优点也是缺点,需要在实际应用中去考量。
观察者模式
定义:观察者模式又叫发布-订阅模式(Publish/Subscribe),定义了一种一对多的依赖关系,让多个观察者同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
观察者模式有两种方模型,分别是推模型和拉模型
观察者模式(Oberver)结构图:
实现:
抽象被观察者角色:定义了动态增加、删除以及通知观察者对象的方法,职责就是管理和通知观察者。持有观察者对象的集合。
具体被观察者角色:一般继承抽象被观察者,实现自己本身的业务逻辑,当状态发生改变时发起通知。
抽象观察者角色:提供一个接口,定义了观察者收到通知时更新自己的方法。
具体观察者角色:实现抽象观察者接口,处理不同具体观察者的不同业务逻辑。
//抽象被观察者
public abstract class Subject {
private List observerList = new ArrayList<>();
public void attach(Observer observer){
observerList.add(observer);
System.out.println("增加了观察者:"+observer.getName);
}
public void dettach(Observer observer){
observerList.remove(observer);
System.out.println("删除了观察者:"+observer.getName);
}
public void notifyObserver(){
for( Observer observer: observerList) {
observer.update("灰太狼要搞事情了");
}
}
}
//具体被观察者
public class Wolf extends Subject {
public void invade() {
System.out.println("灰太狼:我要搞事情了");
notifyObserver();
}
}
//抽象观察者
public interface Observer {
String getName();
void update(String msg);
}
//具体观察者
public PleasantSheep implents Observer {
public String getName() {
return "喜洋洋";
}
public void update(String msg) {
System.out.println("喜洋洋收到通知:"+msg);
}
}
public LazySheep implents Observer {
public String getName() {
return "懒洋洋";
}
public void update(String msg) {
System.out.println("懒洋洋收到通知:"+msg);
}
}
//客户端
public class Client {
public static void main(String[] args) {
//灰太狼--被观察者
Wolf wolf = new Wolf();
//喜洋洋--观察者
Observer pleasantSheep= new PleasantSheep();
//登记观察者
wolf.attach(pleasantSheep);
// 懒羊羊--观察者
Observer lazySheep = new LazySheep();
// 登记观察者
wolf.attach(lazySheep);
//灰太狼入侵
wolf.invade();
}
}
JDK(被观察者【Observable】和观察者【Observer】)
在JAVA语言的 java.util 库里面,提供了一个Observable类以及一个Observer接口,构成JAVA语言对观察者模式的支持。
基于JDK实现:
//被观察者
public class Wolf extends Observable {
private String message;
//TODO
public Wolf(){
System.out.println("灰太狼:我要搞事情了!");
}
public void invade() {
message="大灰狼来了!";
super.setChanged();
super.notifyObservers();
}
}
//观察者
public class PleasantSheep implements Observer {
public void update(Observable o,Object arg) {
System.out.println("喜洋洋接到通知:"+((Wolf)o).getMessage());
}
}
public class LazySheep implements Observer {
public void update(Observable o,Object arg) {
System.out.println("懒洋洋接到通知:"+((Wolf)o).getMessage());
}
}
public class Client {
public static void main(String[] args) {
//被观察者--灰太狼
Wolf wolf = new Wolf();
//观察者--喜洋洋
PleasantSheep pleasantSheep = new PleasantSheep();
//登记观察者
wolf.addObserver(pleasantSheep);
//观察者--懒洋洋
LazySheep lazySheep = new LazySheep();
//登记观察者
wolf.addObserver(lazySheep);
//灰太狼入侵
wolf.invade();
}
}
优点:
(1)观察者和被观察者之间抽象耦合。观察者模式容易扩展,被观察者只持有观察者集合,并不需要知道具体观察者内部的实现。
(2)对象之间的保持高度的协作。当被观察者发生变化时,所有被观察者都会通知到,然后做出相应的动作。
缺点:
(1)如果观察者太多,被观察者通知观察者消耗的时间很多,影响系统的性能。
(2)当观察者集合中的某一观察者错误时就会导致系统卡壳,因此一般会采用异步方式。
(3)抽象通知者还是依赖了抽象观察者,当没有观察者的时候,没办法更新。
(4)要求观察者的所有动作必须一样 ,如果不一样的话,不能实现。
事件委托
一次通知,执行了不同类的不同方法。
PS:Java中是没有像c#delegate关键字的,所以我是通过用Java中的反射来实现。
适配器模式
定义:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
适配器模式主要解决什么问题?
需要的东西就在前面,但却不能使用,而短时间内又无法改造它,于是我们就想办法试配它。
适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。
GOF设计模式中对适配器模式将了两种类型,类适配器和对象适配器。
建议尽量使用对象的适配器模式,少用继承。
何时使用适配器模式?
软件开发后期或维护期。
第三方开发组件。
适配器模式属于补偿模式,专门用来在系统后期扩展、修改时使用,但要注意不要过度使用适配器模式。
//球员
public abstract class Player {
protected Sring name;
public Player(String name){
this.name = name;
}
public abstract void attack();
public abstract void defense();
}
//前锋
public Forward extends Player {
public void attack() {
Systme.out.println("前锋"+name+"进攻");
}
public void defense() {
Systme.out.println("前锋"+name+"防守");
}
}
//中锋
public Center extends Player {
public void attack() {
Systme.out.println("中锋"+name+"进攻");
}
public void defense() {
Systme.out.println("中锋"+name+"防守");
}
}
...
//外籍中锋
public class ForeignCenter {
private String name;
public void 进攻(){
Systme.out.println("外籍中锋"+name+"进攻");
}
public void 防守(){
Systme.out.println("外籍中锋"+name+"防守");
}
}
//翻译
public class Translator extends Player {
private ForeignCenter foreignCenter = new ForeignCenter();
public void attack() {
foreignCenter.进攻();
}
public void defense() {
foreignCenter.防守();
}
}
装饰模式
定义:动态地给一个对象增加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。