一显拜、設(shè)計(jì)模式的原則
1.單一職責(zé)原則
- 一個(gè)類只負(fù)責(zé)一個(gè)功能領(lǐng)域中的相應(yīng)職責(zé)衡奥,或者可以定義為:就一個(gè)類而言爹袁,應(yīng)該只有一個(gè)引起它變化的原因远荠。
- 單一職責(zé)原則是實(shí)現(xiàn)高內(nèi)聚、低耦合的指導(dǎo)方針失息,它是最簡(jiǎn)單但又最難運(yùn)用的原則譬淳,需要設(shè)計(jì)人員發(fā)現(xiàn)類的不同職責(zé)并將其分離,而發(fā)現(xiàn)類的多重職責(zé)需要設(shè)計(jì)人員具有較強(qiáng)的分析設(shè)計(jì)能力和相關(guān)實(shí)踐經(jīng)驗(yàn)盹兢。
2.開閉原則
- 一個(gè)軟件實(shí)體應(yīng)當(dāng)對(duì)擴(kuò)展開放邻梆,對(duì)修改關(guān)閉。即軟件實(shí)體應(yīng)盡量在不修改原有代碼的情況下進(jìn)行擴(kuò)展绎秒。
- 抽象化是開閉原則的關(guān)鍵浦妄。可以為系統(tǒng)定義一個(gè)相對(duì)穩(wěn)定的抽象層见芹,而將不同的實(shí)現(xiàn)行為移至具體的實(shí)現(xiàn)層中完成剂娄。
3.里氏替換原則
- 所有引用基類(父類)的地方必須能透明地使用其子類的對(duì)象。
- 在軟件中將一個(gè)基類對(duì)象替換成它的子類對(duì)象玄呛,程序?qū)⒉粫?huì)產(chǎn)生任何錯(cuò)誤和異常阅懦,反過(guò)來(lái)則不成立,如果一個(gè)軟件實(shí)體使用的是一個(gè)子類對(duì)象的話徘铝,那么它不一定能夠使用基類對(duì)象耳胎。
- 里氏代換原則是實(shí)現(xiàn)開閉原則的重要方式之一,由于使用基類對(duì)象的地方都可以使用子類對(duì)象惕它,因此在程序中盡量使用基類類型來(lái)對(duì)對(duì)象進(jìn)行定義怕午,而在運(yùn)行時(shí)再確定其子類類型,用子類對(duì)象來(lái)替換父類對(duì)象淹魄。
4.依賴倒置原則
- 抽象不應(yīng)該依賴于細(xì)節(jié)郁惜,細(xì)節(jié)應(yīng)當(dāng)依賴于抽象。換言之揭北,要針對(duì)接口編程扳炬,而不是針對(duì)實(shí)現(xiàn)編程
- 在實(shí)現(xiàn)依賴倒轉(zhuǎn)原則時(shí),我們需要針對(duì)抽象層編程搔体,而將具體類的對(duì)象通過(guò)依賴注入(DependencyInjection, DI)的方式注入到其他對(duì)象中恨樟,依賴注入是指當(dāng)一個(gè)對(duì)象要與其他對(duì)象發(fā)生依賴關(guān)系時(shí),通過(guò)抽象來(lái)注入所依賴的對(duì)象疚俱。常用的注入方式有三種劝术,分別是:構(gòu)造注入,設(shè)值注入(Setter注入)和接口注入。構(gòu)造注入是指通過(guò)構(gòu)造函數(shù)來(lái)傳入具體類的對(duì)象养晋,設(shè)值注入是指通過(guò)Setter方法來(lái)傳入具體類的對(duì)象衬吆,而接口注入是指通過(guò)在接口中聲明的業(yè)務(wù)方法來(lái)傳入具體類的對(duì)象。這些方法在定義時(shí)使用的是抽象類型绳泉,在運(yùn)行時(shí)再傳入具體類型的對(duì)象逊抡,由子類對(duì)象來(lái)覆蓋父類對(duì)象。
- 開閉原則是目標(biāo)零酪,里氏代換原則是基礎(chǔ)冒嫡,依賴倒轉(zhuǎn)原則是手段
5.接口隔離原則
- 使用多個(gè)專門的接口,而不使用單一的總接口四苇,即客戶端不應(yīng)該依賴那些它不需要的接口孝凌。
6.合成復(fù)用原則
- 盡量使用對(duì)象組合,而不是繼承來(lái)達(dá)到復(fù)用的目的月腋。
- 通過(guò)繼承來(lái)進(jìn)行復(fù)用的主要問(wèn)題在于繼承復(fù)用會(huì)破壞系統(tǒng)的封裝性蟀架,因?yàn)槔^承會(huì)將基類的實(shí)現(xiàn)細(xì)節(jié)暴露給子類,由于基類的內(nèi)部細(xì)節(jié)通常對(duì)子類來(lái)說(shuō)是可見的榆骚,所以這種復(fù)用又稱“白箱”復(fù)用片拍,如果基類發(fā)生改變,那么子類的實(shí)現(xiàn)也不得不發(fā)生改變寨躁;從基類繼承而來(lái)的實(shí)現(xiàn)是靜態(tài)的穆碎,不可能在運(yùn)行時(shí)發(fā)生改變,沒(méi)有足夠的靈活性
7.迪米特法則
- 一個(gè)軟件實(shí)體應(yīng)當(dāng)盡可能少地與其他實(shí)體發(fā)生相互作用
- 應(yīng)該盡量減少對(duì)象之間的交互职恳,如果兩個(gè)對(duì)象之間不必彼此直接通信所禀,那么這兩個(gè)對(duì)象就不應(yīng)當(dāng)發(fā)生任何直接的相互作用,如果其中的一個(gè)對(duì)象需要調(diào)用另一個(gè)對(duì)象的某一個(gè)方法的話放钦,可以通過(guò)第三者轉(zhuǎn)發(fā)這個(gè)調(diào)用色徘。簡(jiǎn)言之,就是通過(guò)引入一個(gè)合理的第三者來(lái)降低現(xiàn)有對(duì)象之間的耦合度操禀。
二褂策、設(shè)計(jì)模式的分類
- 創(chuàng)建型模式(5種):工廠方法模式,抽象工廠模式颓屑,建造者模式斤寂,原型模式,單例模式揪惦。
- 結(jié)構(gòu)型模式(7種):適配器模式遍搞,裝飾器模式,代理模式器腋,外觀模式溪猿,橋接模式钩杰,組合模式,享元模式诊县。
- 行為型模式(11種):策略模式讲弄、模板方法模式、觀察者模式依痊、迭代子模式避除、責(zé)任鏈模式、命令模式抗悍、備忘錄模式驹饺、狀態(tài)模式钳枕、訪問(wèn)者模式缴渊、中介者模式、解釋器模式鱼炒。
創(chuàng)建型模式
1. 工廠模式(Factory Method)
interface food{}
class A implements food{}
class B implements food{}
class C implements food{}
public class StaticFactory {
private StaticFactory(){}
public static food getA(){ return new A(); }
public static food getB(){ return new B(); }
public static food getC(){ return new C(); }
}
class Client{
//客戶端代碼只需要將相應(yīng)的參數(shù)傳入即可得到對(duì)象
//用戶不需要了解工廠類內(nèi)部的邏輯衔沼。
public void get(String name){
food x = null ;
if ( name.equals("A")) {
x = StaticFactory.getA();
}else if ( name.equals("B")){
x = StaticFactory.getB();
}else {
x = StaticFactory.getC();
}
}
}
2. 抽象工廠模式(Abstract Factory)
- 一個(gè)基礎(chǔ)接口定義了功能,每個(gè)實(shí)現(xiàn)接口的子類就是產(chǎn)品昔瞧,然后定義一個(gè)工廠接口指蚁,實(shí)現(xiàn)了工廠接口的就是工廠,這時(shí)候自晰,接口編程的優(yōu)點(diǎn)就出現(xiàn)了凝化,我們可以新增產(chǎn)品類(只需要實(shí)現(xiàn)產(chǎn)品接口),只需要同時(shí)新增一個(gè)工廠類酬荞,客戶端就可以輕松調(diào)用新產(chǎn)品的代碼搓劫。
- 抽象工廠的靈活性就體現(xiàn)在這里,無(wú)需改動(dòng)原有的代碼混巧,畢竟對(duì)于客戶端來(lái)說(shuō)枪向,靜態(tài)工廠模式在不改動(dòng)StaticFactory類的代碼時(shí)無(wú)法新增產(chǎn)品,如果采用了抽象工廠模式咧党,就可以輕松的新增拓展類秘蛔。
interface food{}
class A implements food{}
class B implements food{}
interface produce{ food get();}
class FactoryForA implements produce{
@Override
public food get() {
return new A();
}
}
class FactoryForB implements produce{
@Override
public food get() {
return new B();
}
}
public class AbstractFactory {
public void ClientCode(String name){
food x= new FactoryForA().get();
x = new FactoryForB().get();
}
}
3. 建造者模式(Builder)
- 當(dāng)一個(gè)類的構(gòu)造函數(shù)參數(shù)個(gè)數(shù)超過(guò)4個(gè),而且這些參數(shù)有些是可選的參數(shù)傍衡,考慮使用構(gòu)造者模式深员。
public class Builder {
static class Student{
String name = null ;
int number = -1 ;
String sex = null ;
int age = -1 ;
String school = null ;
//構(gòu)建器,利用構(gòu)建器作為參數(shù)來(lái)構(gòu)建Student對(duì)象
static class StudentBuilder{
String name = null ;
int number = -1 ;
String sex = null ;
int age = -1 ;
String school = null ;
public StudentBuilder setName(String name) {
this.name = name;
return this ;
}
public StudentBuilder setNumber(int number) {
this.number = number;
return this ;
}
public StudentBuilder setSex(String sex) {
this.sex = sex;
return this ;
}
public StudentBuilder setAge(int age) {
this.age = age;
return this ;
}
public StudentBuilder setSchool(String school) {
this.school = school;
return this ;
}
public Student build() {
return new Student(this);
}
}
public Student(StudentBuilder builder){
this.age = builder.age;
this.name = builder.name;
this.number = builder.number;
this.school = builder.school ;
this.sex = builder.sex ;
}
}
public static void main( String[] args ){
Student a = new Student.StudentBuilder().setAge(13).setName("LiHua").build();
Student b = new Student.StudentBuilder().setSchool("sc").setSex("Male").setName("ZhangSan").build();
}
}
4. 原型模式(Protype)
- 在原型模式中我們可以利用過(guò)一個(gè)原型對(duì)象來(lái)指明我們所要?jiǎng)?chuàng)建對(duì)象的類型蛙埂,然后通過(guò)復(fù)制這個(gè)對(duì)象的方法來(lái)獲得與該對(duì)象一模一樣的對(duì)象實(shí)例倦畅。
- 深拷貝、淺拷貝
深拷貝
public class Prototype implements Cloneable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}finally {
return null;
}
}
public static void main ( String[] args){
Prototype pro = new Prototype();
Prototype pro1 = (Prototype)pro.clone();
}
}
- Object類提供了一個(gè)clone()方法箱残,該方法可以將一個(gè)java對(duì)象復(fù)制一份滔迈,因此在java中可以直接使用clone()方法來(lái)復(fù)制一個(gè)對(duì)象止吁。但是需要實(shí)現(xiàn)clone的Java類必須要實(shí)現(xiàn)一個(gè)接口:Cloneable.該接口表示該類能夠復(fù)制且具體復(fù)制的能力,如果不實(shí)現(xiàn)該接口而直接調(diào)用clone()方法會(huì)拋出CloneNotSupportedException異常燎悍。
5. 單例模式(Singleton)
1). 餓漢法
- 在第一次引用該類的時(shí)候就創(chuàng)建對(duì)象實(shí)例敬惦,而不管實(shí)際是否需要?jiǎng)?chuàng)建。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
2). 單線程寫法
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3). 考慮線程安全的寫法
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
4). 兼顧線程安全和效率的寫法
雙重校驗(yàn)鎖
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
5). 靜態(tài)內(nèi)部類法
//Initialization on Demand Holder
public class Singleton{
private Singleton(){
}
private static class HolderClass{
private final static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return HolderClass.instance;
}
public static void main(String args[]){
Singleton s1, s2;
s1 = Singleton.getInstance();
s2 = Singleton.getInstance();
System.out.println(s1==s2);
}
}
- 由于靜態(tài)單例對(duì)象沒(méi)有作為Singleton的成員變量直接實(shí)例化谈山,因此類加載時(shí)不會(huì)實(shí)例化Singleton俄删,第一次調(diào)用getInstance()時(shí)將加載內(nèi)部類HolderClass,在該內(nèi)部類中定義了一個(gè)static類型的變量instance奏路,此時(shí)會(huì)首先初始化這個(gè)成員變量畴椰,由Java虛擬機(jī)來(lái)保證其線程安全性,確保該成員變量只能初始化一次鸽粉。由于getInstance()方法沒(méi)有任何線程鎖定斜脂,因此其性能不會(huì)造成任何影響。
- 通過(guò)使用IoDH触机,我們既可以實(shí)現(xiàn)延遲加載帚戳,又可以保證線程安全,不影響系統(tǒng)性能儡首,不失為一種最好的Java語(yǔ)言單例模式實(shí)現(xiàn)方式(其缺點(diǎn)是與編程語(yǔ)言本身的特性相關(guān)片任,很多面向?qū)ο笳Z(yǔ)言不支持IoDH)
6). 枚舉寫法
- 這種方式是Effective Java作者Josh Bloch 提倡的方式,它不僅能避免多線程同步問(wèn)題蔬胯,而且還能防止反序列化重新創(chuàng)建新的對(duì)象
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
結(jié)構(gòu)型模式
1. 適配器模式(Adapter)
- 適配器就是一種適配中間件对供,它存在于不匹配的二者之間,用于連接二者氛濒,將不匹配變得匹配产场,簡(jiǎn)單點(diǎn)理解就是平常所見的轉(zhuǎn)接頭,轉(zhuǎn)換器之類的存在泼橘。
1). 類適配器模式
- 當(dāng)我們要訪問(wèn)的接口A中沒(méi)有我們想要的方法 涝动,卻在另一個(gè)接口B中發(fā)現(xiàn)了合適的方法,我們又不能改變?cè)L問(wèn)接口A
- 原理:通過(guò)繼承來(lái)實(shí)現(xiàn)適配器功能炬灭。
public interface Ps2 {
void isPs2();
}
public interface Usb {
void isUsb();
}
public class Usber implements Usb {
@Override
public void isUsb() {
System.out.println("USB口");
}
}
public class Adapter extends Usber implements Ps2 {
@Override
public void isPs2() {
isUsb();
}
}
public class Clienter {
public static void main(String[] args) {
Ps2 p = new Adapter();
p.isPs2();
}
}
2). 對(duì)象適配
- 原理:通過(guò)組合來(lái)實(shí)現(xiàn)適配器功能醋粟。
public class Adapter implements Ps2 {
private Usb usb;
public Adapter(Usb usb){
this.usb = usb;
}
@Override
public void isPs2() {
usb.isUsb();
}
}
public class Clienter {
public static void main(String[] args) {
Ps2 p = new Adapter(new Usber());
p.isUsb();
}
}
3). 接口適配器模式
- 當(dāng)存在這樣一個(gè)接口,其中定義了N多的方法重归,而我們現(xiàn)在卻只想使用其中的一個(gè)到幾個(gè)方法米愿,如果我們直接實(shí)現(xiàn)接口,那么我們要對(duì)所有的方法進(jìn)行實(shí)現(xiàn)鼻吮,會(huì)導(dǎo)致這個(gè)類變得臃腫育苟,調(diào)用也不方便,這時(shí)我們可以使用一個(gè)抽象類作為中間件椎木,即適配器违柏,用這個(gè)抽象類實(shí)現(xiàn)接口博烂,而在抽象類中所有的方法都進(jìn)行置空,那么我們?cè)趧?chuàng)建抽象類的繼承類漱竖,而且重寫我們需要使用的那幾個(gè)方法即可禽篱。
- 原理:通過(guò)抽象類來(lái)實(shí)現(xiàn)適配
public interface A {
void a();
void b();
void c();
void d();
void e();
void f();
}
public abstract class Adapter implements A {
public void a(){}
public void b(){}
public void c(){}
public void d(){}
public void e(){}
public void f(){}
}
public class Ashili extends Adapter {
public void a(){
System.out.println("實(shí)現(xiàn)A方法被調(diào)用");
}
public void d(){
System.out.println("實(shí)現(xiàn)d方法被調(diào)用");
}
}
public class Clienter {
public static void main(String[] args) {
A a = new Ashili();
a.a();
a.d();
}
}
2.裝飾模式
- 裝飾器模式在不影響各個(gè)ConcreteComponent核心價(jià)值的同時(shí),添加了他特有的裝飾效果馍惹,具備非常好的通用性
//基礎(chǔ)接口
public interface Component {
public void biu();
}
//具體實(shí)現(xiàn)類
public class ConcretComponent implements Component {
public void biu() {
System.out.println("biubiubiu");
}
}
//裝飾類
public class Decorator implements Component {
public Component component;
public Decorator(Component component) {
this.component = component;
}
public void biu() {
this.component.biu();
}
}
//具體裝飾類
public class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
public void biu() {
System.out.println("ready?go!");
this.component.biu();
}
}
//使用裝飾器
Component component = new ConcreteDecorator(new ConcretComponent());
component.biu();
//console:
ready?go!
biubiubiu
3. 代理模式
- 通過(guò)代理對(duì)象訪問(wèn)目標(biāo)對(duì)象.這樣做的好處是:可以在目標(biāo)對(duì)象實(shí)現(xiàn)的基礎(chǔ)上,增強(qiáng)額外的功能操作,即擴(kuò)展目標(biāo)對(duì)象的功能.
- 這里使用到編程中的一個(gè)思想:不要隨意去修改別人已經(jīng)寫好的代碼或者方法,如果需改修改,可以通過(guò)代理的方式來(lái)擴(kuò)展該方法
- 代理模式的關(guān)鍵點(diǎn)是:代理對(duì)象與目標(biāo)對(duì)象.代理對(duì)象是對(duì)目標(biāo)對(duì)象的擴(kuò)展,并會(huì)調(diào)用目標(biāo)對(duì)象
1).靜態(tài)代理
- 靜態(tài)代理在使用時(shí),需要定義接口或者父類,被代理對(duì)象與代理對(duì)象一起實(shí)現(xiàn)相同的接口或者是繼承相同父類.
/**
* 接口
*/
public interface IUserDao {
void save();
}
/**
* 接口實(shí)現(xiàn)
* 目標(biāo)對(duì)象
*/
public class UserDao implements IUserDao {
public void save() {
System.out.println("----已經(jīng)保存數(shù)據(jù)!----");
}
}
/**
* 代理對(duì)象,靜態(tài)代理
*/
public class UserDaoProxy implements IUserDao{
//接收保存目標(biāo)對(duì)象
private IUserDao target;
public UserDaoProxy(IUserDao target){
this.target=target;
}
public void save() {
System.out.println("開始事務(wù)...");
target.save();//執(zhí)行目標(biāo)對(duì)象的方法
System.out.println("提交事務(wù)...");
}
}
/**
* 測(cè)試類
*/
public class App {
public static void main(String[] args) {
//目標(biāo)對(duì)象
UserDao target = new UserDao();
//代理對(duì)象,把目標(biāo)對(duì)象傳給代理對(duì)象,建立代理關(guān)系
UserDaoProxy proxy = new UserDaoProxy(target);
proxy.save();//執(zhí)行的是代理的方法
}
}
- 可以做到在不修改目標(biāo)對(duì)象的功能前提下,對(duì)目標(biāo)功能擴(kuò)展躺率。
- 缺點(diǎn): 因?yàn)榇韺?duì)象需要與目標(biāo)對(duì)象實(shí)現(xiàn)一樣的接口,所以會(huì)有很多代理類,類太多.同時(shí),一旦接口增加方法,目標(biāo)對(duì)象與代理對(duì)象都要維護(hù).
2). 動(dòng)態(tài)代理
- 特點(diǎn):
1>. 代理對(duì)象,不需要實(shí)現(xiàn)接口
2>. 代理對(duì)象的生成,是利用JDK的API,動(dòng)態(tài)的在內(nèi)存中構(gòu)建代理對(duì)象(需要我們指定創(chuàng)建代理對(duì)象/目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類型)
3>. 動(dòng)態(tài)代理也叫做:JDK代理,接口代理
/**
* 創(chuàng)建動(dòng)態(tài)代理對(duì)象
* 動(dòng)態(tài)代理不需要實(shí)現(xiàn)接口,但是需要指定接口類型
*/
public class ProxyFactory{
//維護(hù)一個(gè)目標(biāo)對(duì)象
private Object target;
public ProxyFactory(Object target){
this.target=target;
}
//給目標(biāo)對(duì)象生成代理對(duì)象
public Object getProxyInstance(){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("開始事務(wù)2");
//執(zhí)行目標(biāo)對(duì)象方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事務(wù)2");
return returnValue;
}
}
);
}
}
/**
* 測(cè)試類
*/
public class App {
public static void main(String[] args) {
// 目標(biāo)對(duì)象
IUserDao target = new UserDao();
// 【原始的類型 class cn.itcast.b_dynamic.UserDao】
System.out.println(target.getClass());
// 給目標(biāo)對(duì)象,創(chuàng)建代理對(duì)象
IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
// class $Proxy0 內(nèi)存中動(dòng)態(tài)生成的代理對(duì)象
System.out.println(proxy.getClass());
// 執(zhí)行方法 【代理對(duì)象】
proxy.save();
}
}
行為型模式
1. 模板方法模式
- 模板方法模式在一個(gè)方法中定義一個(gè)算法的骨架万矾,而將一些步驟的實(shí)現(xiàn)延遲到子類中悼吱。模板方法使得子類可以在不改變算法結(jié)構(gòu)的情況下,重新定義算法中某些步驟的具體實(shí)現(xiàn)良狈。
- 注意幾點(diǎn):
- 保護(hù)抽象類中定義算法順序的方法不被子類修改后添。
- 分離可變及不可變部分,讓子類自己決定可變部分的實(shí)現(xiàn)们颜。
- 讓算法的具體實(shí)現(xiàn)對(duì)子類開放吕朵,對(duì)其他類關(guān)閉。
package com.singland.dp.template;
public abstract class DriveTemplate {
public final void drive() {
openDoor();
startEngine();
gear();
go();
if (music()) {
mp3();
}
brake();
stop();
}
protected abstract void openDoor();
protected void startEngine() {
System.out.println("engine started !");
}
protected abstract void gear();
protected void go() {
System.out.println("running...");
}
private void mp3() {
System.out.println("music is good");
}
protected boolean music() {
return false;
}
protected abstract void brake();
protected void stop() {
System.out.println("stopped !");
}
}
package com.singland.dp.template;
public class SuzukiScross extends DriveTemplate {
@Override
protected void openDoor() {
System.out.println("keyless entry");
}
@Override
protected void gear() {
System.out.println("gear with hand");
}
@Override
protected void brake() {
System.out.println("brake with foot");
}
@Override
protected boolean music() {
return true;
}
}
- JDBC代碼操作數(shù)據(jù)庫(kù)
package com.studentinfomgt.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
public abstract class JdbcTemplate2 {
@Autowired
private DataSource dataSource;
private Connection connection;
protected PreparedStatement statement;
protected ResultSet resultSet;
public final void dbOperation(String sql, Object entity) throws SQLException {
getStatement(sql);
crud(entity);
releaseResources();
}
protected void getStatement(String sql) throws SQLException {
connection = dataSource.getConnection();
this.statement = connection.prepareStatement(sql);
}
protected abstract void crud(Object entity) throws SQLException;
private void releaseResources() throws SQLException {
if (resultSet != null)
resultSet.close();
if (statement != null)
statement.close();
if (connection != null)
connection.close();
}
}
package com.studentinfomgt.dao;
import java.sql.SQLException;
import com.studentinfomgt.pojo.Student;
public class JdbcCreateEntity extends JdbcTemplate2 {
@Override
protected void crud(Object entity) throws SQLException {
Student student = (Student) entity;
statement.setString(1, student.getId());
statement.setString(2, student.getStudentNumber());
statement.setString(3, student.getFirstName());
statement.setString(4, student.getLastName());
statement.setString(5, student.getGender());
statement.setInt(6, student.getAge());
statement.setString(7, student.getClassName());
statement.setString(8, student.getMajor());
statement.execute();
}
}
2. 訪問(wèn)者模式
- 將作用于某種數(shù)據(jù)結(jié)構(gòu)中的各元素的操作分離出來(lái)封裝成獨(dú)立的類窥突,使其在不改變數(shù)據(jù)結(jié)構(gòu)的前提下可以添加作用于這些元素的新的操作,為數(shù)據(jù)結(jié)構(gòu)中的每個(gè)元素提供多種訪問(wèn)方式硫嘶。它將對(duì)數(shù)據(jù)的操作與數(shù)據(jù)結(jié)構(gòu)進(jìn)行分離阻问,是行為類模式中最復(fù)雜的一種模式。
- 訪問(wèn)者中的元素:
- 抽象訪問(wèn)者(Visitor)角色:定義一個(gè)訪問(wèn)具體元素的接口敞恋,為每個(gè)具體元素類對(duì)應(yīng)一個(gè)訪問(wèn)操作 visit() 甸鸟,該操作中的參數(shù)類型標(biāo)識(shí)了被訪問(wèn)的具體元素块攒。
- 具體訪問(wèn)者(ConcreteVisitor)角色:實(shí)現(xiàn)抽象訪問(wèn)者角色中聲明的各個(gè)訪問(wèn)操作,確定訪問(wèn)者訪問(wèn)一個(gè)元素時(shí)該做什么刨秆。
- 抽象元素(Element)角色:聲明一個(gè)包含接受操作 accept() 的接口,被接受的訪問(wèn)者對(duì)象作為 accept() 方法的參數(shù)忆畅。
- 具體元素(ConcreteElement)角色:實(shí)現(xiàn)抽象元素角色提供的 accept() 操作衡未,其方法體通常都是 visitor.visit(this) ,另外具體元素中可能還包含本身業(yè)務(wù)邏輯的相關(guān)操作家凯。
/**
* 訪問(wèn)者接口
*/
public interface IVisitor {
void visit(FreeCourse freeCourse);
void visit(CodingCourse codingCourse);
}
/**
* 訪問(wèn)者的具體實(shí)現(xiàn)
*/
public class Visitor implements IVisitor {
@Override
public void visit(FreeCourse freeCourse) {
System.out.println("free course: " + freeCourse.getName());
}
@Override
public void visit(CodingCourse codingCourse) {
System.out.println("coding course: " + codingCourse.getName() + " price: " + codingCourse.getPrice());
}
}
/**
* 抽象類缓醋,課程
*/
public abstract class Course {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
abstract void accept(IVisitor visitor);
}
/**
* 具體的實(shí)體類,免費(fèi)課程
*/
public class FreeCourse extends Course {
@Override
void accept(IVisitor visitor) {
visitor.visit(this);
}
}
/**
* 具體的實(shí)現(xiàn)類绊诲,實(shí)戰(zhàn)課程
*/
public class CodingCourse extends Course {
private int price;
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
void accept(IVisitor visitor) {
visitor.visit(this);
}
}
/**
* 測(cè)試類
*/
public class App {
@Test
public void test(){
//初始化容器
List<Course> courses = new ArrayList<>();
//構(gòu)建課程實(shí)現(xiàn)
FreeCourse freeCourse = new FreeCourse();
freeCourse.setName("SpringMVC.");
CodingCourse codingCourse = new CodingCourse();
codingCourse.setName("Java design pattern.");
codingCourse.setPrice(299);
//裝填
courses.add(freeCourse);
courses.add(codingCourse);
//訪問(wèn)
for (Course course : courses) {
course.accept(new Visitor());
}
}
}
- 優(yōu)點(diǎn):
- 擴(kuò)展性好送粱。能夠在不修改對(duì)象結(jié)構(gòu)中的元素的情況下,為對(duì)象結(jié)構(gòu)中的元素添加新的功能掂之。
- 復(fù)用性好抗俄〈喽。可以通過(guò)訪問(wèn)者來(lái)定義整個(gè)對(duì)象結(jié)構(gòu)通用的功能,從而提高系統(tǒng)的復(fù)用程度动雹。靈活性好偎快。訪問(wèn)者模式將數(shù)據(jù)結(jié)構(gòu)與作用于結(jié)構(gòu)上的操作解耦,使得操作集合可相對(duì)自由地演化而不影響系統(tǒng)的數(shù)據(jù)結(jié)構(gòu)洽胶。
- 符合單一職責(zé)原則晒夹。訪問(wèn)者模式把相關(guān)的行為封裝在一起,構(gòu)成一個(gè)訪問(wèn)者姊氓,使每一個(gè)訪問(wèn)者的功能都比較單一丐怯。
- 缺點(diǎn):
- 增加新的元素類很困難。在訪問(wèn)者模式中翔横,每增加一個(gè)新的元素類读跷,都要在每一個(gè)具體訪問(wèn)者類中增加相應(yīng)的具體操作,這違背了“開閉原則”禾唁。
- 破壞封裝效览。訪問(wèn)者模式中具體元素對(duì)訪問(wèn)者公布細(xì)節(jié),這破壞了對(duì)象的封裝性荡短,具體元素變更比較麻煩丐枉。違反了依賴倒置原則。
- 訪問(wèn)者模式依賴了具體類掘托,而沒(méi)有依賴抽象類瘦锹。