设计模式的本质:
模式区分:
单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。
适配器模式、桥接模式、装饰模式、组合模式、外观模式、亨元模式、代理模式。
模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。
OOP七大原则:
单例模式:
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
一个全局使用的类频繁地创建与销毁。
1 . 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
2 . 避免对资源的多重占用(比如写文件操作)。
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
1 . 要求生产唯一序列号。
2 . WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
3 . 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
getInstance( ) 方法中需要使用同步锁 synchronized ( Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。
// 饿汉式单例
public class Hungry {
// 浪费空间
private byte[ ] data1 = new byte[ 1024 *1024] ;
private byte[ ] data2 = new byte[ 1024 *1024] ;
private byte[ ] data3 = new byte[ 1024 *1024] ;
private Hungry ( ) {
}
private final static Hungry HUNGRY = new Hungry( ) ;
public static Hungry getInstance ( ) {
return HUNGRY;
}
}
//
public class LazyMan {
private LazyMan ( ) {
System.out.println( Thread.currentThread( ) .getName( ) + " OK" ) ;
}
private static LazyMan lazyMan;
public static LazyMan getInstance ( ) {
if( lazyMan == null) {
lazyMan = new LazyMan( ) ;
}
return lazyMan;
}
// 多线程并发
public static void main( String[ ] args) {
for ( int i = 0 ; i < 10 ; i++) {
new Thread(( ) - > {
LazyMan.getInstance( ) ;
}, String.valueOf( i)) .start( ) ;
}
}
}
public static LazyMan getInstance ( ) {
if( lazyMan == null) {
synchronized ( LazyMan.class) {
if( lazyMan == null) {
lazyMan = new LazyMan( ) ;
}
}
}
return lazyMan;
}
private volatile static LazyMan lazyMan;
public static LazyMan getInstance ( ) {
if( lazyMan == null) {
synchronized ( LazyMan.class) {
if( lazyMan == null) {
lazyMan = new LazyMan( ) ; // 不是原子性操作
/**
* 创建对象后进行的操作:
* 1 . 分配内存空间
* 2 . 执行构造方法, 初始化对象
* 3 . 把这个对象指向这个空间
* 在这里会发生指令重排现象, 所以需要加 volatile 关键字
*/
}
}
}
return lazyMan;
}
public class LazyMan {
private LazyMan ( ) {
synchronized ( LazyMan.class) {
if( lazyMan != null) {
throw new RuntimeException( "不要试图通过反射破坏单例" ) ;
}
}
System.out.println( Thread.currentThread( ) .getName( ) + " OK" ) ;
}
private volatile static LazyMan lazyMan;
public static LazyMan getInstance ( ) {
if( lazyMan == null) {
synchronized ( LazyMan.class) {
if( lazyMan == null) {
lazyMan = new LazyMan( ) ;
}
}
}
return lazyMan;
}
// 多线程并发
public static void main( String[ ] args) throws Exception{
LazyMan instance = LazyMan.getInstance( ) ;
Constructor< LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor( null) ;
declaredConstructor.setAccessible( true) ;
LazyMan instance2 = declaredConstructor.newInstance( ) ;
System.out.println( instance) ;
System.out.println( instance2) ;
}
}
public static void main( String[ ] args) throws Exception{
//LazyMan instance = LazyMan.getInstance( ) ;
Constructor< LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor( null) ;
declaredConstructor.setAccessible( true) ;
LazyMan instance = declaredConstructor.newInstance( ) ;
LazyMan instance2 = declaredConstructor.newInstance( ) ;
System.out.println( instance) ;
System.out.println( instance2) ;
}
private static boolean bzw = false ;
private LazyMan ( ) {
synchronized ( LazyMan.class) {
if( bzw == false ) {
bzw = true ;
} else {
throw new RuntimeException( "不要试图通过反射破坏单例" ) ;
}
}
System.out.println( Thread.currentThread( ) .getName( ) + " OK" ) ;
}
// 枚举本身是一个 CLASS 类
// 枚举类 底层有一个 ( String, int) 的构造方法, 通过反编译工具可以看出
public enum EnumSingle {
INSTANCE ;
public EnumSingle getInstance ( ) {
return INSTANCE ;
}
}
class Test{
public static void main( String[ ] args) throws Exception {
EnumSingle instance1 = EnumSingle.INSTANCE ;
Constructor< EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor( String.class, int.class) ;
declaredConstructor.setAccessible( true) ;
EnumSingle instance2 = declaredConstructor.newInstance( ) ;
System.out.println( instance1) ;
System.out.println( instance2) ;
}
}
// 静态内部类
public class Holder {
private Holder ( ) {
}
public static Holder getInstance ( ) {
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder( ) ;
}
}
工厂模式:
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
实现了创建者和调用者的分离
1 . 实例化对象不使用new,用工厂方法代替
2 . 将选择实现类,创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。
主要解决接口选择的问题。
1 . 一个调用者想创建一个对象,只要知道其名称就可以了。
2 . 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
3 . 屏蔽产品的具体实现,调用者只关心产品的接口。
每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
1 . 日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
2 . 数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
3 . 设计一个连接服务器的框架,需要三个协议,"POP3" 、"IMAP" 、"HTTP" ,可以把这三个作为产品类,共同实现一个接口。
作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
public interface Shape {
void draw( ) ;
}
public class Rectangle implements Shape {
@Override
public void draw ( ) {
System.out.println( "Inside Rectangle::draw() method." ) ;
}
}
public class Square implements Shape {
@Override
public void draw ( ) {
System.out.println( "Inside Square::draw() method." ) ;
}
}
public class Circle implements Shape {
@Override
public void draw ( ) {
System.out.println( "Inside Circle::draw() method." ) ;
}
}
public class ShapeFactory {
//使用 getShape 方法获取形状类型的对象
public Shape getShape( String shapeType) {
if( shapeType == null) {
return null;
}
if( shapeType.equalsIgnoreCase( "CIRCLE" )) {
return new Circle( ) ;
} else if( shapeType.equalsIgnoreCase( "RECTANGLE" )) {
return new Rectangle( ) ;
} else if( shapeType.equalsIgnoreCase( "SQUARE" )) {
return new Square( ) ;
}
return null;
}
}
public class FactoryPatternDemo {
public static void main( String[ ] args) {
ShapeFactory shapeFactory = new ShapeFactory( ) ;
//获取 Circle 的对象,并调用它的 draw 方法
Shape shape1 = shapeFactory.getShape( "CIRCLE" ) ;
//调用 Circle 的 draw 方法
shape1.draw( ) ;
//获取 Rectangle 的对象,并调用它的 draw 方法
Shape shape2 = shapeFactory.getShape( "RECTANGLE" ) ;
//调用 Rectangle 的 draw 方法
shape2.draw( ) ;
//获取 Square 的对象,并调用它的 draw 方法
Shape shape3 = shapeFactory.getShape( "SQUARE" ) ;
//调用 Square 的 draw 方法
shape3.draw( ) ;
}
}
Inside Circle::draw( ) method.
Inside Rectangle::draw( ) method.
Inside Square::draw( ) method.
抽象工厂模式:
该超级工厂又称为其他工厂的工厂。在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
主要解决接口选择的问题。
当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
1 . QQ 换皮肤,一整套一起换。
2 . 生成不同操作系统的程序。
产品族难扩展,产品等级易扩展。
public interface Shape {
void draw( ) ;
}
public class Rectangle implements Shape {
@Override
public void draw ( ) {
System.out.println( "Inside Rectangle::draw() method." ) ;
}
}
public class Square implements Shape {
@Override
public void draw ( ) {
System.out.println( "Inside Square::draw() method." ) ;
}
}
public class Circle implements Shape {
@Override
public void draw ( ) {
System.out.println( "Inside Circle::draw() method." ) ;
}
}
public interface Color {
void fill( ) ;
}
public class Red implements Color {
@Override
public void fill ( ) {
System.out.println( "Inside Red::fill() method." ) ;
}
}
public class Green implements Color {
@Override
public void fill ( ) {
System.out.println( "Inside Green::fill() method." ) ;
}
}
public class Blue implements Color {
@Override
public void fill ( ) {
System.out.println( "Inside Blue::fill() method." ) ;
}
}
public abstract class AbstractFactory {
public abstract Color getColor( String color) ;
public abstract Shape getShape( String shape) ;
}
public class ShapeFactory extends AbstractFactory {
@Override
public Shape getShape( String shapeType) {
if( shapeType == null) {
return null;
}
if( shapeType.equalsIgnoreCase( "CIRCLE" )) {
return new Circle( ) ;
} else if( shapeType.equalsIgnoreCase( "RECTANGLE" )) {
return new Rectangle( ) ;
} else if( shapeType.equalsIgnoreCase( "SQUARE" )) {
return new Square( ) ;
}
return null;
}
@Override
public Color getColor( String color) {
return null;
}
}
public class ColorFactory extends AbstractFactory {
@Override
public Shape getShape( String shapeType) {
return null;
}
@Override
public Color getColor( String color) {
if( color == null) {
return null;
}
if( color.equalsIgnoreCase( "RED" )) {
return new Red( ) ;
} else if( color.equalsIgnoreCase( "GREEN" )) {
return new Green( ) ;
} else if( color.equalsIgnoreCase( "BLUE" )) {
return new Blue( ) ;
}
return null;
}
}
public class FactoryProducer {
public static AbstractFactory getFactory( String choice) {
if( choice.equalsIgnoreCase( "SHAPE" )) {
return new ShapeFactory( ) ;
} else if( choice.equalsIgnoreCase( "COLOR" )) {
return new ColorFactory( ) ;
}
return null;
}
}
public class AbstractFactoryPatternDemo {
public static void main( String[ ] args) {
//获取形状工厂
AbstractFactory shapeFactory = FactoryProducer.getFactory( "SHAPE" ) ;
//获取形状为 Circle 的对象
Shape shape1 = shapeFactory.getShape( "CIRCLE" ) ;
//调用 Circle 的 draw 方法
shape1.draw( ) ;
//获取形状为 Rectangle 的对象
Shape shape2 = shapeFactory.getShape( "RECTANGLE" ) ;
//调用 Rectangle 的 draw 方法
shape2.draw( ) ;
//获取形状为 Square 的对象
Shape shape3 = shapeFactory.getShape( "SQUARE" ) ;
//调用 Square 的 draw 方法
shape3.draw( ) ;
//获取颜色工厂
AbstractFactory colorFactory = FactoryProducer.getFactory( "COLOR" ) ;
//获取颜色为 Red 的对象
Color color1 = colorFactory.getColor( "RED" ) ;
//调用 Red 的 fill 方法
color1.fill( ) ;
//获取颜色为 Green 的对象
Color color2 = colorFactory.getColor( "Green" ) ;
//调用 Green 的 fill 方法
color2.fill( ) ;
//获取颜色为 Blue 的对象
Color color3 = colorFactory.getColor( "BLUE" ) ;
//调用 Blue 的 fill 方法
color3.fill( ) ;
}
}
Inside Circle::draw( ) method.
Inside Rectangle::draw( ) method.
Inside Square::draw( ) method.
Inside Red::fill( ) method.
Inside Green::fill( ) method.
Inside Blue::fill( ) method.
建造者模式:
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
主要解决在软件系统中,有时候面临着"一个复杂对象" 的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
1 . 建造者独立,易扩展。
2 . 便于控制细节风险。
1 . 产品必须有共同点,范围有限制。
2 . 如内部变化复杂,会有很多的建造类。
1 . 需要生成的对象具有复杂的内部结构。
2 . 需要生成的对象内部属性本身相互依赖。
与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。
public interface Item {
public String name( ) ;
public Packing packing( ) ;
public float price( ) ;
}
public interface Packing {
public String pack( ) ;
}
public class Wrapper implements Packing {
@Override
public String pack ( ) {
return "Wrapper" ;
}
}
public class Bottle implements Packing {
@Override
public String pack ( ) {
return "Bottle" ;
}
}
public abstract class Burger implements Item {
@Override
public Packing packing ( ) {
return new Wrapper( ) ;
}
@Override
public abstract float price( ) ;
}
public abstract class ColdDrink implements Item {
@Override
public Packing packing ( ) {
return new Bottle( ) ;
}
@Override
public abstract float price( ) ;
}
public class VegBurger extends Burger {
@Override
public float price ( ) {
return 25 .0f;
}
@Override
public String name ( ) {
return "Veg Burger" ;
}
}
public class ChickenBurger extends Burger {
@Override
public float price ( ) {
return 50 .5f;
}
@Override
public String name ( ) {
return "Chicken Burger" ;
}
}
public class Coke extends ColdDrink {
@Override
public float price ( ) {
return 30 .0f;
}
@Override
public String name ( ) {
return "Coke" ;
}
}
public class Pepsi extends ColdDrink {
@Override
public float price ( ) {
return 35 .0f;
}
@Override
public String name ( ) {
return "Pepsi" ;
}
}
public class Meal {
private List< Item> items = new ArrayList< Item> ( ) ;
public void addItem( Item item) {
items.add( item) ;
}
public float getCost ( ) {
float cost = 0 .0f;
for ( Item item : items) {
cost += item.price( ) ;
}
return cost;
}
public void showItems ( ) {
for ( Item item : items) {
System.out.print( "Item : " +item.name( )) ;
System.out.print( ", Packing : " +item.packing( ) .pack( )) ;
System.out.println( ", Price : " +item.price( )) ;
}
}
}
public class MealBuilder {
public Meal prepareVegMeal ( ) {
Meal meal = new Meal( ) ;
meal.addItem( new VegBurger( )) ;
meal.addItem( new Coke( )) ;
return meal;
}
public Meal prepareNonVegMeal ( ) {
Meal meal = new Meal( ) ;
meal.addItem( new ChickenBurger( )) ;
meal.addItem( new Pepsi( )) ;
return meal;
}
}
public class BuilderPatternDemo {
public static void main( String[ ] args) {
MealBuilder mealBuilder = new MealBuilder( ) ;
Meal vegMeal = mealBuilder.prepareVegMeal( ) ;
System.out.println( "Veg Meal" ) ;
vegMeal.showItems( ) ;
System.out.println( "Total Cost: " +vegMeal.getCost( )) ;
Meal nonVegMeal = mealBuilder.prepareNonVegMeal( ) ;
System.out.println( "\n \n Non-Veg Meal" ) ;
nonVegMeal.showItems( ) ;
System.out.println( "Total Cost: " +nonVegMeal.getCost( )) ;
}
}
Veg Meal
Item : Veg Burger, Packing : Wrapper, Price : 25.0
Item : Coke, Packing : Bottle, Price : 30.0
Total Cost: 55.0
Non-Veg Meal
Item : Chicken Burger, Packing : Wrapper, Price : 50.5
Item : Pepsi, Packing : Bottle, Price : 35.0
Total Cost: 85.5
原型模式:
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
在运行期建立和删除原型。
1 . 性能提高。
2 . 逃避构造函数的约束。
1 . 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。
2 . 必须实现 Cloneable 接口。
1 . 资源优化场景。
2 . 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
3 . 性能和安全要求的场景。
4 . 通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
5 . 一个对象多个修改者的场景。
6 . 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
7 . 在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。
与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。
@Override
protected Object clone( ) throws CloneNotSupportedException {
Object obj = super.clone( ) ;
Demo t = (( Shape) obj) .getDemo( ) ;
/ / 将这个对象的属性也进行克隆
(( Shape) obj) .setDemo(( Demo) t.clone( )) ;
return obj;
}
@Data
public class Person implements Serializable {
private String name;
private Integer age;
private Address address;
public Person deepClone ( ) {
Person p2 = null;
Person p1 = this;
PipedOutputStream out = new PipedOutputStream( ) ;
PipedInputStream in = new PipedInputStream( ) ;
try {
in.connect( out) ;
} catch ( IOException e) {
e.printStackTrace( ) ;
}
try( ObjectOutputStream bo = new ObjectOutputStream( out) ;
ObjectInputStream bi = new ObjectInputStream( in) ; ) {
bo.writeObject( p1) ;
p2 = ( Person) bi.readObject( ) ;
} catch ( Exception e) {
e.printStackTrace( ) ;
}
return p2;
}
}
public abstract class Shape implements Cloneable {
private String id ;
protected String type ;
abstract void draw( ) ;
public String getType ( ) {
return type ;
}
public String getId ( ) {
return id ;
}
public void setId( String id ) {
this.id = id ;
}
public Object clone ( ) {
Object clone = null;
try {
clone = super.clone( ) ;
} catch ( CloneNotSupportedException e) {
e.printStackTrace( ) ;
}
return clone;
}
}
public class Rectangle extends Shape {
public Rectangle ( ) {
type = "Rectangle" ;
}
@Override
public void draw ( ) {
System.out.println( "Inside Rectangle::draw() method." ) ;
}
}
public class Square extends Shape {
public Square ( ) {
type = "Square" ;
}
@Override
public void draw ( ) {
System.out.println( "Inside Square::draw() method." ) ;
}
}
public class ShapeCache {
private static Hashtable< String, Shape> shapeMap
= new Hashtable< String, Shape> ( ) ;
public static Shape getShape( String shapeId) {
Shape cachedShape = shapeMap.get( shapeId) ;
return ( Shape) cachedShape.clone( ) ;
}
// 对每种形状都运行数据库查询,并创建该形状
// shapeMap.put( shapeKey, shape) ;
// 例如,我们要添加三种形状
public static void loadCache ( ) {
Square square = new Square( ) ;
square.setId( "2" ) ;
shapeMap.put( square.getId( ) ,square) ;
Rectangle rectangle = new Rectangle( ) ;
rectangle.setId( "3" ) ;
shapeMap.put( rectangle.getId( ) ,rectangle) ;
}
}
public class PrototypePatternDemo {
public static void main( String[ ] args) {
ShapeCache.loadCache( ) ;
Shape clonedShape2 = ( Shape) ShapeCache.getShape( "2" ) ;
System.out.println( "Shape : " + clonedShape2.getType( )) ;
Shape clonedShape3 = ( Shape) ShapeCache.getShape( "3" ) ;
System.out.println( "Shape : " + clonedShape3.getType( )) ;
}
}
Shape : Square
Shape : Rectangle
适配器模式:
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。
这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。
主要解决在软件系统中,常常要将一些"现存的对象" 放到新的环境中,而新环境要求的接口是现对象不能满足的。
1 . 可以让任何两个没有关联的类一起运行。
2 . 提高了类的复用。
3 . 增加了类的透明度。
4 . 灵活性好。
1 . 过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
2 . 由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。
有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。
适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。
public interface MediaPlayer {
public void play( String audioType, String fileName) ;
}
public interface AdvancedMediaPlayer {
public void playVlc( String fileName) ;
public void playMp4( String fileName) ;
}
public class VlcPlayer implements AdvancedMediaPlayer{
@Override
public void playVlc( String fileName) {
System.out.println( "Playing vlc file. Name: " + fileName) ;
}
@Override
public void playMp4( String fileName) {
//什么也不做
}
}
public class Mp4Player implements AdvancedMediaPlayer{
@Override
public void playVlc( String fileName) {
//什么也不做
}
@Override
public void playMp4( String fileName) {
System.out.println( "Playing mp4 file. Name: " + fileName) ;
}
}
public class MediaAdapter implements MediaPlayer {
AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter( String audioType) {
if( audioType.equalsIgnoreCase( "vlc" ) ) {
advancedMusicPlayer = new VlcPlayer( ) ;
} else if ( audioType.equalsIgnoreCase( "mp4" )) {
advancedMusicPlayer = new Mp4Player( ) ;
}
}
@Override
public void play( String audioType, String fileName) {
if( audioType.equalsIgnoreCase( "vlc" )) {
advancedMusicPlayer.playVlc( fileName) ;
} else if( audioType.equalsIgnoreCase( "mp4" )) {
advancedMusicPlayer.playMp4( fileName) ;
}
}
}
public class AudioPlayer implements MediaPlayer {
MediaAdapter mediaAdapter;
@Override
public void play( String audioType, String fileName) {
//播放 mp3 音乐文件的内置支持
if( audioType.equalsIgnoreCase( "mp3" )) {
System.out.println( "Playing mp3 file. Name: " + fileName) ;
}
//mediaAdapter 提供了播放其他文件格式的支持
else if( audioType.equalsIgnoreCase( "vlc" )
|| audioType.equalsIgnoreCase( "mp4" )) {
mediaAdapter = new MediaAdapter( audioType) ;
mediaAdapter.play( audioType, fileName) ;
}
else{
System.out.println( "Invalid media. " +
audioType + " format not supported" ) ;
}
}
}
public class AdapterPatternDemo {
public static void main( String[ ] args) {
AudioPlayer audioPlayer = new AudioPlayer( ) ;
audioPlayer.play( "mp3" , "beyond the horizon.mp3" ) ;
audioPlayer.play( "mp4" , "alone.mp4" ) ;
audioPlayer.play( "vlc" , "far far away.vlc" ) ;
audioPlayer.play( "avi" , "mind me.avi" ) ;
}
}
Playing mp3 file. Name: beyond the horizon.mp3
Playing mp4 file. Name: alone.mp4
Playing vlc file. Name: far far away.vlc
Invalid media. avi format not supported
桥接模式:
桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。
这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。
在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。
1 . 抽象和实现的分离。
2 . 优秀的扩展能力。
3 . 实现细节对客户透明。
桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
1 . 如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
2 . 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
3 . 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
对于两个独立变化的维度,使用桥接模式再适合不过了。
public interface DrawAPI {
public void drawCircle( int radius, int x, int y) ;
}
public class RedCircle implements DrawAPI {
@Override
public void drawCircle( int radius, int x, int y) {
System.out.println( "Drawing Circle[ color: red, radius: "
+ radius +", x: " +x+", " + y +"]" ) ;
}
}
public class GreenCircle implements DrawAPI {
@Override
public void drawCircle( int radius, int x, int y) {
System.out.println( "Drawing Circle[ color: green, radius: "
+ radius +", x: " +x+", " + y +"]" ) ;
}
}
public abstract class Shape {
protected DrawAPI drawAPI;
protected Shape( DrawAPI drawAPI) {
this.drawAPI = drawAPI;
}
public abstract void draw( ) ;
}
public class Circle extends Shape {
private int x, y, radius;
public Circle( int x, int y, int radius, DrawAPI drawAPI) {
super( drawAPI) ;
this.x = x;
this.y = y;
this.radius = radius;
}
public void draw ( ) {
drawAPI.drawCircle( radius,x,y) ;
}
}
public class BridgePatternDemo {
public static void main( String[ ] args) {
Shape redCircle = new Circle( 100,100 , 10 , new RedCircle( )) ;
Shape greenCircle = new Circle( 100,100 , 10 , new GreenCircle( )) ;
redCircle.draw( ) ;
greenCircle.draw( ) ;
}
}
Drawing Circle[ color: red, radius: 10 , x: 100 , 100 ]
Drawing Circle[ color: green, radius: 10 , x: 100 , 100 ]
代理模式:
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。
在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。
在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
1 . 职责清晰。
2 . 高扩展性。
3 . 智能化。
1 . 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
2 . 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
1 . 远程代理。
2 . 虚拟代理。
3 . Copy-on-Write 代理。
4 . 保护(Protect or Access)代理。
5 . Cache代理。
6 . 防火墙(Firewall)代理。
7 . 同步化(Synchronization)代理。
8 . 智能引用(Smart Reference)代理。
1 . 和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
2 . 和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
public interface Image {
void display( ) ;
}
public class RealImage implements Image {
private String fileName;
public RealImage( String fileName) {
this.fileName = fileName;
loadFromDisk( fileName) ;
}
@Override
public void display ( ) {
System.out.println( "Displaying " + fileName) ;
}
private void loadFromDisk( String fileName) {
System.out.println( "Loading " + fileName) ;
}
}
public class ProxyImage implements Image{
private RealImage realImage;
private String fileName;
public ProxyImage( String fileName) {
this.fileName = fileName;
}
@Override
public void display ( ) {
if( realImage == null) {
realImage = new RealImage( fileName) ;
}
realImage.display( ) ;
}
}
public class ProxyPatternDemo {
public static void main( String[ ] args) {
Image image = new ProxyImage( "test_10mb.jpg" ) ;
// 图像将从磁盘加载
image.display( ) ;
System.out.println( "" ) ;
// 图像不需要从磁盘加载
image.display( ) ;
}
}
Loading test_10mb.jpg
Displaying test_10mb.jpg
Displaying test_10mb.jpg
Java动态代理:
1 .InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。
2 .Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法。
1 . 可以使真实角色的操作更加纯碎,不用去关注一些公共业务。
2 . 公共交给代理角色,实现了业务分工。
3 . 公共业务发生扩展的时候,方便集中管理。
4 . 一个动态代理类代理的是一个接口,一般就是对应的一类业务。
5 . 一个动态代理类可以代理多个类,只要是实现了同一个接口即可。
public interface People {
String work( ) ;
}
public class Teacher implements People {
@Override
public String work ( ) {
System.out.println( "我是老师" ) ;
return "老师在工作" ;
}
}
public class WorkHandler implements InvocationHandler {
// 代理类中的真实对象
private Object obj;
public Object getProxy ( ) {
/**
* 通过Proxy类的newProxyInstance方法创建代理对象,我们来看下方法中的参数
* 第一个参数:people.getClass( ) .getClassLoader( ) ,使用handler对象的classloader对象来加载我们的代理对象
* 第二个参数:people.getClass( ) .getInterfaces( ) ,这里为代理类提供的接口是真实对象实现的接口,这样代理对象就能像真实对象一样调用接口中的所有方法
* 第三个参数:handler,我们将代理对象关联到上面的InvocationHandler对象上
*/
Class clazz = obj.getClass( ) ;
ClassLoader classLoader = clazz.getClassLoader( ) ;
Class[ ] interfaces = clazz.getInterfaces( ) ;
return Proxy.newProxyInstance( classLoader, interfaces, this) ;
}
// 构造函数,给我们的真实对象赋值
public WorkHandler( Object obj) {
this.obj = obj;
}
/**
* proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
* method:我们所要调用某个对象真实的方法的Method对象
* args:指代代理对象方法传递的参数
*/
@Override
public Object invoke( Object proxy, Method method, Object[ ] args) throws Throwable {
//在真实的对象执行之前我们可以添加自己的操作
System.out.println( "before invoke。。。" ) ;
Object invoke = method.invoke( obj, args) ;
//在真实的对象执行之后我们可以添加自己的操作
System.out.println( "after invoke。。。" ) ;
return invoke;
}
}
public class TestDemo {
public static void main( String[ ] args) {
// 要代理的真实对象
People people = new Teacher( ) ;
WorkHandler handler = new WorkHandler( people) ;
People proxy = ( People) handler.getProxy( ) ;
System.out.println( proxy.work( )) ;
}
}
before invoke。。。
我是老师
after invoke。。。
老师在工作
1 . getInvocationHandler:返回指定代理实例的调用处理程序
2 . getProxyClass:给定类加载器和接口数组的代理类的java.lang.Class对象。
3 . isProxyClass:当且仅当使用getProxyClass方法或newProxyInstance方法将指定的类动态生成为代理类时,才返回true。
4 . newProxyInstance:返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。