复习各种模式的时候的心得
Liskov可替换原则
更强的不变量、更弱的前置条件、更强的后置条件
委托
委托的意思就是,我们想要实现一个方法,我们不用A类去实现,而是另外写一个类B,让这个类B去实现,然后我们在A类中实例化类B,然后调用B类中的方法,主要好处就是充分实现单一职责原则。(应对场景:比如有鸟这个类,我们有会叫的鸟,叫的方式有很多种,会飞的鸟,飞的方式也有很多种,如果我们用一个类鸟,然后用许多类来继承鸟这个类,我们最后就很容易组合爆炸。但是我们可以用将飞和叫都封装成类,然后在鸟这个类里面实例化,这样我们就想用什么飞法和叫法我们就将他复制成什么就好了)
interface Flyable {//飞的接口
public void fly();
}
interface Quackable {//叫的接口
public void quack();
}
class FlyWithWings implements Flyable { //某种飞的方式
@Override
public void fly() {
System.out.println("fly with wings");
}
}
class Quack implements Quackable {//某种叫的方式
@Override
public void quack() {
System.out.println("quack like duck");
}
}
interface Ducklike extends Flyable, Quackable {}
public class Duck implements Ducklike {
Flyable flyBehavior;//我们在这个动物的类里面实例化飞和叫
Quackable quackBehavior;
void setFlyBehavior(Flyable f) {//是哪种飞我就赋值成哪种
this.flyBehavior = f;
}
void setQuackBehavior(Quackable q) {//是哪种叫我就赋值成哪种
this.quackBehavior = q;
}
@Override
public void fly() {//飞就调用飞的方法
this.flyBehavior.fly();
}
@Override
public void quack() {//叫就调用叫的方法
this.quackBehavior.quack();
}
}
结构型模式
适配器模式
我们写了很多个类来实现同一个功能,但是实现方式不一样,这就造成接口也大不相同,但是用户端一般都是希望只是用一个接口,就能完成所有的功能,所以,我们就应该将这些类装化成一个接口,统一的开放给用户。(应用场景:比如我们用一个显示图形的类,比如我们在显示长方形的时候是按照一个点的x,y值和宽高进行显示,但是在用户那边习惯用长方形的对角顶点来输入,这时候,我们就需要用到适配器模式,其实就是将用户输入的参数做一些调整,然后匹配我们的方法)
interface Shape {
void display(int x1, int y1, int x2, int y2);
}
class Rectangle implements Shape {//但是用户不这么输入,所以我们就应该调整使得适配
void display(int x1, int y1, int x2, int y2) {
new LegacyRectangle().display(x1, y1, x2-x1, y2-y1);
}
}
class LegacyRectangle {//原来我们选择显示长方形的方法
void display(int x1, int y1, int w, int h) {...}
}
class Client {
Shape shape = new Rectangle();
public display() {//在用户端我们调用适配器的方法就可以正常使用了
shape.display(x1, y1, x2, y2);
}
}
装饰器模式
我们在创建完一个类之后,如果我们要在类中新增加一些功能的时候,我们通常是利用继承,但是如果功能很多,并且每个功能有分成了不同的实现方式呢?这就造成了一个问题,组合爆炸。所以这时候我们就会用到装饰器模式,再不改变原来的类的前提下,进行增加功能。(应用场景:比如我们有一个形状的类,我们实现了各种形状,比如正方形、长方形、圆形等,但是每一种形状在图形化展示的时候应该有颜色,而且又有各种不同的颜色,难道我们要对每种颜色继承每一个类来实现吗?答案肯定是否定的。这时候,我们就用装饰器模式。)
public interface Shape {
void draw();
}
public class Rectangle implements Shape {//长方形的类
@Override
public void draw() {
System.out.println("Shape: Rectangle");
}
}
public class Circle implements Shape {//圆形的类
@Override
public void draw() {
System.out.println("Shape: Circle");
}
}
public abstract class ShapeDecorator implements Shape {//我们要增加颜色功能的话
protected Shape decoratedShape;
public ShapeDecorator(Shape decoratedShape){
this.decoratedShape = decoratedShape;
}
public void draw(){
decoratedShape.draw();
}
}
public class RedShapeDecorator extends ShapeDecorator {//对于每一种颜色,不用管是什么形状,只需要看颜色即可
public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
}
@Override
public void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
}
private void setRedBorder(Shape decoratedShape){
System.out.println("Border Color: Red");
}
}
外观模式
我们用很多种类,但是用户肯定是分不清那个类是哪个类的,这时候我们就需要用一个类把这些类包含在一起,然后我们用这一个类调用别的类的方法,来实现功能就好了(比如:我们有MySQL和Oracle数据库这两个类,我们需要分别进行查找等操作,在用户端,我们就用一个类,然后对用户的输入进行判断,看用户想用哪种数据库类的操作,我们就实例化一个类,然后调用该类的操作)
public class MySqlHelper {//MySQL类
public static Connection getMySqlDBConnection() {…}
public void generateMySqlPDFReport(String tableName, Connection con){…}
public void generateMySqlHTMLReport(String tableName, Connection con){…}
}
public class OracleHelper {//Oracle类
public static Connection getOracleDBConnection() {…}
public void generateOraclePDFReport(String tableName, Connection con){…}
public void generateOracleHTMLReport(String tableName, Connection con){…}
}
//在用户端我们只需要给他一个类,然后对用户的输入进行判断就好了
public class HelperFacade {
public static void generateReport(DBTypes dbType, ReportTypes reportType, String tableName){
Connection con = null;
switch (dbType){
case MYSQL:
con = MySqlHelper.getMySqlDBConnection();
MySqlHelper mySqlHelper = new MySqlHelper();
switch(reportType){
case HTML:
mySqlHelper.generateMySqlHTMLReport(tableName, con);
break;
case PDF:
mySqlHelper.generateMySqlPDFReport(tableName, con);
break;
}
break;
case ORACLE: …
}
public static enum DBTypes { MYSQL,ORACLE; }
public static enum ReportTypes { HTML,PDF;}
}
代理模式
我们做摸个操作的时候代价比较高,这是我们就可以用到代理模式,在不需要这个操作的时候,就先用一个代理站位,等到真正需要执行操作的时候再去实例化。(应用场景:比如我们需要从磁盘中读一个文件,但是文件很大,如果在程序一开始我们就读进去的话占用很大的内存空间,我们就可以使用代理模式)
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() {…}
private void loadFromDisk(String fileName){…}
}
public class ProxyImage implements Image {//代理类
private Image 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 interface PaymentStrategy {
public void pay(int amount);
}
public class CreditCardStrategy implements PaymentStrategy {//信用卡支付
@Override
public void pay(int amount) {
System.out.println(amount +" paid with credit card");
}
}
public class PaypalStrategy implements PaymentStrategy {//Paypal支付
@Override
public void pay(int amount) {
System.out.println(amount + " paid using Paypal.");
}
}
public class ShoppingCart {
...
public void pay(PaymentStrategy paymentMethod){//用户想要什么样的支付,就把PaymentStrategy实例化成哪种方式
paymentMethod.pay(amount);
}
}
模板模式
一些相似的类,我们将公共的方法提取到一个抽象类中,模板方法用final,然后对每一种差异化的行为我们在子类中重写(应用场景:比如我们有游戏类,对应的游戏有棋类游戏,球类游戏等,每一种游戏肯定有开始游戏、结束游戏、初始化游戏、玩游戏等功能,玩游戏这个其实就是调用开始游戏、结束游戏、初始化游戏,所以他就是一个模板方法,然后我们只需要在不同的子类中重写开始游戏、结束游戏、初始化游戏这三个方法就可以了)
public abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
public final void play(){//模板
initialize();
startPlay();
endPlay();
}
}
public class Cricket extends Game {//重写差异化的方法
@Override
void endPlay() {
System.out.println("Cricket Game Finished!");
}
@Override
void initialize() {
System.out.println("Cricket Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Cricket Game Started. Enjoy the game!");
}
}
public class Football extends Game {
@Override
void endPlay() {
System.out.println("Football Game Finished!");
}
@Override
void initialize() {
System.out.println("Football Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Football Game Started. Enjoy the game!");
}
}
迭代器模式
我们以类中的某个属性,对类进行遍历而使用的模式。主要就是需要实现next()和hasNext()方法。
public interface Iterator {//迭代器接口
public boolean hasNext();
public Object next();
}
public interface Container {
public Iterator getIterator();
}
public class NameRepository implements Container {
public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};
@Override
public Iterator getIterator() {
return new NameIterator();
}
private class NameIterator implements Iterator {//重写迭代器
int index;
@Override
public boolean hasNext() {
if(index < names.length){
return true;
}
return false;
}
@Override
public Object next() {
if(this.hasNext()){
return names[index++];
}
return null;
}
}
}
观察者模式
相当于是偶像和粉丝之间的关系,偶像的一举一动粉丝都应该知道,如果粉丝加入到偶像的粉丝团,那么偶像也应该知道该粉丝团中加入了这个粉丝。
public class Subject {//偶像
private List<Observer> observers = new ArrayList<Observer>();//偶像的粉丝团
private int state;//偶像的状态
public int getState() {return state;}
public void setState(int state) {//相当于偶像发了个微博,然后所有的粉丝就应该都能收到
this.state = state;
notifyAllObservers();
}
public void attach(Observer observer){observers.add(observer);}//粉丝团加入粉丝
private void notifyAllObservers(){
for (Observer observer : observers) {
observer.update();
}
}
}
public abstract class Observer {//粉丝的抽象类
protected Subject subject;
public abstract void update();
}
public class BinaryObserver extends Observer{//一种粉丝
public BinaryObserver(Subject subject){//粉丝初始化的时候应该有偶像
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println( "Binary String: " +Integer.toBinaryString( subject.getState() ) );
}
}
访问者模式
在特定ADT上执行某种特定操作,但该操作不在ADT内部实现,而是delegate到独立的visitor对象,客户端可灵活扩展/改变visitor的操作算法,而不影响ADT(应用场景:比如我要去买东西,可以买菜也可以买书,但是众所周知,菜是按斤买的,书是按本买的,所以计价方式不一样,但是我们都是要去买,这时候就可以用到访问者模式)
public interface ItemElement {//买东西的时候,东西也知道是谁买的
public int accept(ShoppingCartVisitor visitor);
}
public class Book implements ItemElement{//买书
private double price;
...
int accept(ShoppingCartVisitor visitor) {
visitor.visit(this);
}
}
public class Fruit implements ItemElement{//买水果
private double weight;
...
int accept(ShoppingCartVisitor visitor) {
visitor.visit(this);
}
}
public interface ShoppingCartVisitor { //人去买东西
int visit(Book book);
int visit(Fruit fruit);
}
public class ShoppingCartVisitorImpl implements ShoppingCartVisitor {//一种买东西的计价方式
public int visit(Book book) {
int cost=0;
if(book.getPrice() > 50){
cost = book.getPrice()-5;
}else
cost = book.getPrice();
System.out.println("Book ISBN::"+book.getIsbnNumber() + " cost ="+cost);
return cost;
}
public int visit(Fruit fruit) {
int cost = fruit.getPricePerKg()*fruit.getWeight();
System.out.println(fruit.getName() + " cost = "+cost);
return cost;
}
}
public class ShoppingCartClient {
public static void main(String[] args) {
ItemElement[] items = new ItemElement[]{new Book(20, "1234"),new Book(100, "5678"),new Fruit(10, 2, "Banana"), new Fruit(5, 5, "Apple")};
int total = calculatePrice(items);
System.out.println("Total Cost = "+total);
}
private static int calculatePrice(ItemElement[] items) {
ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
int sum=0;
for(ItemElement item : items)//买的东西调用accept方法,在accept方法中调用visitor.visit
sum = sum + item.accept(visitor);
return sum;
}
}
状态模式
这个模式设计主要就是为了不使用if else语句,因为如果要是使用if else的话,如果状态转移图有一些变化,那么我们就可能需要大量的改if else语句,但是在状态模式中,我们只需要改一些内部的状态转移函数就好了(应用场景:比如我们有一个自动机,当在S1状态下输入a转移到S2,输入b不动,在S2状态下输入a或b都转移到S1,如果输入的是其他,两个状态都直接报错,我们就可以应用状态模式)
class Context {//我们具体的一个对象
State state;//状态
public Context(State s) {state = s;}
public void move(char c) { state = state.move(c); }
public boolean accept() { return state.accept(); }
public State getState() { return this.state; }
}
public interface State {//状态模式的接口
State move(char c);
boolean accept();
}
class State1 implements State {//其中的一个状态
static State1 instance = new State1();
private State1() {}
public State move (char c) {//状态的转移
switch (c) {
case 'a': return State2.instance;
case 'b': return State1.instance;
default: throw new IllegalArgumentException();
}
}
public boolean accept() {
return false;
}
}
class State2 implements State {//另一个状态
static State2 instance = new State2();
private State2() {}
public State move (char c) {//状态的转移
switch (c) {
case 'a': return State1.instance;
case 'b': return State1.instance;
default: throw new IllegalArgumentException();
}
}
public boolean accept() {return true;}
}
备忘录模式
其实就是相当于我们经常进行的一个操作ctrl+z
public class Memento {//备忘录,我们能够存储一个状态
private String state;
public Memento(String state){
this.state = state;
}
public String getState(){
return state;
}
}
public class Originator {
private String state;
public void setState(String state){
this.state = state;
}
public String getState(){
return state;
}
public Memento saveStateToMemento(){//将状态存进备忘录里面
return new Memento(state);
}
public void getStateFromMemento(Memento Memento){//取出来
state = Memento.getState();
}
}
public class CareTaker {
private List<Memento> mementoList = new ArrayList<Memento>();//有一堆备忘录,想要那个我们就取哪个
public void add(Memento state){
mementoList.add(state);
}
public Memento get(int index){
return mementoList.get(index);
}
}
创建型模式
工厂模式
我们对一个接口有很多种实现,人但是我们并不知道用户想用哪一种,这时候就需要工厂模式,我们通过用户的输入来确定返回某一种具体的类。(应用场景:比如说我们有两种路径查找方式,一种是文件查找,一种是系统查找,但是我们并不知道用户想用哪种,这时候我们就把接口留出来,让用户去选择)
public interface Trace {
public void setDebug( boolean debug );
public void debug( String message );
public void error( String message );
}
public class FileTrace implements Trace {//两种具体的实现方式FileTrace
private PrintWriter pw;
private boolean debug;
public FileTrace() throws IOException {
pw = new PrintWriter( new FileWriter( "t.log" ) );
}
public void setDebug( boolean debug ) {
this.debug = debug;
}
public void debug( String message ) {
if( debug ) {
pw.println( "DEBUG: " + message );
pw.flush();
}
}
public void error( String message ) {
pw.println( "ERROR: " + message );
pw.flush();
}
}
public class SystemTrace implements Trace {//SystemTrace
private boolean debug;
public void setDebug( boolean debug ) {
this.debug = debug;
}
public void debug( String message ) {
if( debug )
System.out.println( "DEBUG: " + message );
}
public void error( String message ) {
System.out.println( "ERROR: " + message );
}
}
public class Factory1 implements TraceFactory {//工厂1,只能生产SystemTrace
public Trace getTrace() {
return new SystemTrace();
}
}
public class Factory2 implements TraceFactory {//工厂2,两种都能生产
public getTrace(String type) {
if(type.equals(“file”)
return new FileTrace();
else if (type.equals(“system”)
return new SystemTrace();
}
}
抽象工厂模式
我们的一个工厂可能不止能生产一种商品,我们可能能生产很多不同种类的商品(应用场景:我们或一个图形,图形的样子可能是圆形和方形,颜色可能是红色和蓝色,但是我们规定圆形只能是红色,方形只能是蓝色,这时候我们就不应该让用户自己去选择图形和颜色的搭配,我们应该先搭配好在让用户去选择,这有一种套餐的感觉。)
public interface Window{
public void setTitle(String s);
public void repaint();
}
public class PMWindow implements Window{
public void setTitle(){…}
public void repaint(){…}
}
public class MotifWindow implements Window{
public void setTitle(){…}
public void repaint(){…}
}
public interface AbstractWidgetFactory{//抽象工厂
public Window createWindow();
public Scrollbar createScrollbar();
}
public class WidgetFactory1{//固定搭配1
public Window createWindow(){
return new MSWindow();
}
public Scrollbar createScrollbar(){A}
}
public class WidgetFactory2{//固定搭配2
public Window createWindow(){
return new MotifWindow();
}
public Scrollbar createScrollbar(){B}
}
参考:菜鸟教程;HIT软件构造PPT;设计模式