一、设计模式
1.设计模式分类
常用设计模式可以分为三类创建模式、结构模式、行为模式
创建模式:Factory、Builder、Singleton
结构模式:Adapter、Decorator、Flyweight、Proxy
行为模式:State、Memento、Observer、Strategy、Visitor
2.工厂模式
案例:我们经常需要new 一些Dao类,或在new Dao之前做一些准备工作,可以用工厂模式。
要点:把创建工作交给工厂类实现
public class FactoryTest
{
public static void main(String[] args)
{
UserDao uDao = (UserDao)DaoFactory.getDao("test.UserDao");
uDao.login();
uDao.logout();
}
}
//面向接口编程习惯
interface IDao
{
void login();
}
//实现类,里面除了实现接口方法还可以有自己的方法
class UserDao implements IDao
{
@Override
public void login()
{
System.out.println("user login!");
}
public void logout()
{
System.out.println("user logout!");
}
}
//工厂类主要负责类对象的创建
class DaoFactory
{
public static IDao getDao(String DaoName)
{
IDao dao = null;
try
{
dao = (IDao)Class.forName(DaoName).newInstance();
}
catch (Exception e)
{
e.printStackTrace();
}
return dao;
}
}
3.建造模式
案例:假设一个复杂的对象由多个部件组成,并且改变的机会较大,我们可以考虑将该对象的创建分开实现,部件的创建和部件的组合
要点:一个类专门负责部件创建,另一个类专门负责部件的组装
public class BuilderTest
{
public static void main(String[] args)
{
Combination combination = new Combination(new Create());
combination.buile();
}
}
// 负责创建部件的类
class Create
{
// 创建A部件
public String createA()
{
return "A";
}
// 创建B部件
public String createB()
{
return "B";
}
// 创建C部件
public String createC()
{
return "C";
}
}
//负责组合部件的类
class Combination
{
private Create creater;
public Combination(Create create)
{
this.creater = create;
}
//组装部件
public void buile()
{
String str = creater.createA()+creater.createB()+creater.createC();
System.out.println(str);
}
}
4.单例模式
案例:在一个项目中我们用到读取文件的类,这个类的使用又不是很频繁我们可以考虑用单例模式去做。
要点:在整个系统中保证该对象只有一个实例
public class SingletonTest
{
public static void main(String[] args)
{
FileOpe fo1 = FileOpe.getInstance();
fo1.createFile();
FileOpe fo2 = FileOpe.getInstance();
fo2.createFile();
}
}
class FileOpe
{
private static FileOpe fo = null;
//这里保证不管有多少个线程系统中只有一个该类实例
public synchronized static FileOpe getInstance()
{
if(fo == null)
{
fo = new FileOpe();
}
return fo;
}
//私有构造方法是关键确保类的实例化只能在该类内部进行
private FileOpe()
{
System.out.println("constructor going !");
}
public void createFile()
{
System.out.println("create File !");
}
}
5.适配器模式
案例:编写一个类图像清晰化A,同时清晰化之后就要为图像去噪声B
要点:把扩展模块注入到主模块中去使用(单项适配器模式)
public class AdapterTest
{
public static void main(String[] args)
{
SubA suba = new SubA(new SubB());
suba.funA();
}
}
//主模块
class A
{
public void funA()
{
System.out.println("图像清晰化");
}
}
//扩展模块
interface B
{
void funB();
}
class SubB implements B
{
@Override
public void funB()
{
System.out.println("图像去噪音");
}
}
//重写主模块,把扩展模块注入到主模块中
class SubA extends A
{
private B b;
public SubA(B b)
{
this.b = b;
}
@Override
public void funA()
{
super.funA();
b.funB();
}
}
案例:编写一个读取字符串的类ReadString,之后又要增加新的功能把读到的字符串转换成大写字母使用。
要点:把主模块注入到扩展模块
public class DecoratorTest
{
public static void main(String[] args)
{
//这里可以类比一下文件读写操作 BufferedReader br=new BufferedReader(new FileReader(new File("")));
Change change = new Change(new ReadString());
change.changeString();
}
}
//定义主模块接口以便于主模块的扩展
interface IRead
{
void read();
}
//主模块
class ReadString implements IRead
{
@Override
public void read()
{
System.out.println("读取字符串");
}
}
//扩展模块
class Change
{
private IRead iRead;
public Change(IRead iRead)
{
this.iRead = iRead;
}
public void changeString()
{
iRead.read();
System.out.println("转换为大写字母");
}
}
7.代理模式
案例:西门庆要勾搭潘金莲,需要王婆做代理 。
要点:表面上是西门庆跟王婆勾搭,其实是和潘金莲勾搭。
public class ProxyTest
{
public static void main(String[] args)
{
Wangpo wangpo = new Wangpo();
wangpo.makeEyesWithMan();
wangpo.happyWithman();
}
}
//定义王婆、潘金莲一类的女人
interface KindWomen
{
void makeEyesWithMan();
void happyWithman();
}
//定义潘金莲是什么样的女人
class PanJinLian implements KindWomen
{
@Override
public void makeEyesWithMan()
{
System.out.println("潘金莲抛媚眼。");
}
@Override
public void happyWithman()
{
System.out.println("潘金莲happy中...");
}
}
//定义王婆是什么样的女人
class Wangpo implements KindWomen
{
KindWomen women;
//默认情况下让潘金莲代理
public Wangpo()
{
this.women = new PanJinLian();
}
//可以让任何一个女人代理
public Wangpo(KindWomen women)
{
this.women = women;
}
@Override
public void makeEyesWithMan()
{
women.makeEyesWithMan();//实际上上让别人代理做事
}
@Override
public void happyWithman()
{
women.happyWithman();//实际上上让别人代理做事
}
}
8.享元模式
案例:一些文字软件中经常会出现大量相同的字,每个就是一个对象,我们可以使用享元模式使这些字共同有一个对象表示。
要点:避免大量拥有相同内容的小类的开销,使大家共同使用一个对象。
public class Flyweight
{
public static void main(String[] args)
{
Word w1 = WordPool.getWorld("1","刘德华");
Word w2 = WordPool.getWorld("2","张学友");
Word w3 = WordPool.getWorld("1","刘德华");
Word w4 = WordPool.getWorld("1","刘德华");
}
}
//享元类
class Word
{
private String content;
private String key;
public Word(String content,String key)
{
this.content = content;
this.key = key;
System.out.println("构造实例"+key);
}
}
//享元池,可以用map存贮
class WordPool
{
private static Map<String,Word> pool = new HashMap<String, Word>();
public static Word getWorld(String key,String content)
{
Word word = pool.get(key);
if(word == null)
{
word = new Word(key,content);
pool.put(key, word);
}
return word;
}
}
9.观察者模式
案例:某商店的商品价格变化时要通知该商店的会员朋友
要点:观察者和被观察者互不认识
public class ObserverTest
{
public static void main(String[] args)
{
Product p = new Product("小白菜",5);
Customer c = new Customer();
p.addObserver(c);//调用继承Observable的方法添加观察者可以添加多个。
p.changePrice(7);
}
}
class Product extends Observable
{
public String pname;
public double price;
public Product(String pname,double price)
{
this.pname = pname;
this.price = price;
}
//商品加个发生变化
public void changePrice(double price)
{
this.price = price;
this.setChanged();//设置变化点通知观察者价格变化
this.notifyObservers("价格变化");//通知所有的观察者一个消息,这里会自动调用观察者的Update方法。
}
}
//观察者
class Customer implements Observer
{
//继承方法第一个参数为被观察的对象,第二个参数为发来的消息
@Override
public void update(Observable obs, Object obj)
{
System.out.println(((Product)obs).pname+":"+obj.toString());
}
}
10.策略模式
案例:赵云拿着三个锦囊去东吴给刘备娶老婆,锦囊里面是诸葛亮给的三个妙计
要点:用不同策略装锦囊,锦囊就可以做不同的事。
public class StrategyTest
{
public static void main(String[] args)
{
//赵云执行方法
Context context ;
context = new Context(new BackDoor());
context.operate();
context = new Context(new GivenGreenLight());
context.operate();
context = new Context(new BlockEnemy());
context.operate();
}
}
//首先定义一个策略的接口
interface IStrategy
{
//每个策略里面都要有一个可执行的算法
void operate();
}
//策略1:找乔国老帮忙
class BackDoor implements IStrategy
{
//策略方法
@Override
public void operate()
{
System.out.println("找乔国老帮忙,让吴国太给孙权施加压力");
}
}
//策略2:求吴国太
class GivenGreenLight implements IStrategy
{
@Override
public void operate()
{
System.out.println("求吴国太开绿灯,放行");
}
}
//策略3:孙夫人断后
class BlockEnemy implements IStrategy
{
@Override
public void operate()
{
System.out.println("孙夫人断后,阻击追兵");
}
}
//锦囊:存放策略
class Context
{
private IStrategy strategy;
public Context(IStrategy strategy)
{
this.strategy = strategy;
}
//执行策略
public void operate()
{
strategy.operate();
}
}
二、类与类之间的关系
1.继承关系
继承指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力。在Java中继承关系通过关键字extends明确标识,在设计时一般没有争议性。在UML类图设计中,继承用一条带空心三角箭头的实线表示,从子类指向父类,或者子接口指向父接口。
2.实现关系
实现指的是一个class类实现interface接口(可以是多个)的功能,实现是类与接口之间最常见的关系。在Java中此类关系通过关键字implements明确标识,在设计时一般没有争议性。在UML类图设计中,实现用一条带空心三角箭头的虚线表示,从类指向实现的接口。
3.依赖关系
简单的理解,依赖就是一个类A使用到了另一个类B,而这种使用关系是具有偶然性的、临时性的、非常弱的,但是类B的变化会影响到类A。比如某人要过河,需要借用一条船,此时人与船之间的关系就是依赖。表现在代码层面,为类B作为参数被类A在某个method方法中使用。在UML类图设计中,依赖关系用由类A指向类B的带箭头虚线表示。
4.关联关系
关联体现的是两个类之间语义级别的一种强依赖关系,比如我和我的朋友,这种关系比依赖更强、不存在依赖关系的偶然性、关系也不是临时性的,一般是长期性的,而且双方的关系一般是平等的。关联可以是单向、双向的。表现在代码层面,为被关联类B以类的属性形式出现在关联类A中,也可能是关联类A引用了一个类型为被关联类B的全局变量。在UML类图设计中,关联关系用由关联类A指向被关联类B的带箭头实线表示,在关联的两端可以标注关联双方的角色和多重性标记。
5.聚合关系
聚合是关联关系的一种特例,它体现的是整体与部分的关系,即has-a的关系。此时整体与部分之间是可分离的,它们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享。比如计算机与CPU、公司与员工的关系等,比如一个航母编队包括海空母舰、驱护舰艇、舰载飞机及核动力攻击潜艇等。表现在代码层面,和关联关系是一致的,只能从语义级别来区分。在UML类图设计中,聚合关系以空心菱形加实线箭头表示。
6.组合关系
组合也是关联关系的一种特例,它体现的是一种contains-a的关系,这种关系比聚合更强,也称为强聚合。它同样体现整体与部分间的关系,但此时整体与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束,比如人和人的大脑。表现在代码层面,和关联关系是一致的,只能从语义级别来区分。在UML类图设计中,组合关系以实心菱形加实线箭头表示。
7.总结
对于继承、实现这两种关系没多少疑问,它们体现的是一种类和类、或者类与接口间的纵向关系。其他的四种关系体现的是类和类、或者类与接口间的引用、横向关系,是比较难区分的,有很多事物间的关系要想准确定位是很难的。前面也提到,这四种关系都是语义级别的,所以从代码层面并不能完全区分各种关系,但总的来说,后几种关系所表现的强弱程度依次为:组合>聚合>关联>依赖。