W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
[TOC]
什么是JAVA設(shè)計模式以及作用?
設(shè)計模式是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過分類編目的、代碼設(shè)計經(jīng)驗的總結(jié)
使用設(shè)計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性,本篇將介紹10種JAVA常用設(shè)計模式
## 1.JAVA 設(shè)計模式 - 單例設(shè)計模式
單例模式是一種創(chuàng)建模式,這種模式只涉及一個單獨的類,它負(fù)責(zé)創(chuàng)建自己的對象
該類確保只創(chuàng)建單個對象,這個類提供了一種訪問其唯一對象的方法
單例模式目的是為了整個應(yīng)用中有且只有一個實例,所有指向該類型實例的引用都指向這個實例
保證對象不能再類的外部被隨意實例化,解決方法:將構(gòu)造器進行私有化處理
保證類創(chuàng)建的過程發(fā)生在類的內(nèi)部,還有保證在類的外部能拿到在類的內(nèi)部初始化的對象
單例模式類型:餓漢式單例,懶漢式單例
Singleton(餓漢模式)
餓漢模式,特點是程序加載類的時候比較慢,但運行時獲得對象的速度比較快,它從加載到應(yīng)用結(jié)束會一直占用資源
餓漢模式代碼:
class Singleton {
public class SingletonDemo {
public static void main(String[] args) {
Singleton1 s1 = Singleton1.getInstance();
Singleton1 s2 = Singleton1.getInstance();
System.out.println(s1==s2); //true
Singleton2 s3 = Singleton2.getInstance();
Singleton2 s4 = Singleton2.getInstance();
System.out.println(s3==s4); //true
}
}
class Singleton1{
private static final Singleton1 instance = new Singleton1(); //在內(nèi)部準(zhǔn)備好一個對象
public static Singleton1 getInstance(){
return instance;
}
private Singleton1(){}
public void show(){
System.out.println("Singleton1");
}
}
class Singleton2 {
private static Singleton2 instance;
//將instance傳遞到外部去
public static Singleton2 getInstance(){
if(instance == null){
instance = new Singleton2();
}
return instance;
}
private Singleton2(){}
}
Singleton(懶漢模式)
懶漢模式特點,程序是運行時獲得對象的速度比較慢,但加載類的時候比較快
它在整個應(yīng)用的生命周期只有一部分時間在占用資源
懶漢模式代碼:
class Singleton{
private static Singleton instance = null;
public static Singleton getInstance(){// 將instance傳遞到外部去
if(instance == null){
instance = new Singleton();
}
return instance;
}
private Singleton(){}
}
public static Singleton2 getInstance(){
if(instance == null){
synchronized(Singleton2.class){
if(instance == null){
instance = new Singleton2();
}
}
}
return instance;
}
Singleton(餓漢模式) & Singleton(懶漢模式) 區(qū)別
(1)這兩種模式對于初始化較快,占用資源少的輕量級對象來說,沒有多大的性能差異,選擇懶漢式或餓漢式都沒有問題
但是對于初始化慢,占用資源多的重 量級對象來說,就會有比較明顯的差別了
所以,對重量級對象應(yīng)用餓漢模式,類加載時速度慢,但運行時速度快;懶漢模式則與之相反,類加載時速度快,但運行時第一次獲得對象的速度慢
(2)從用戶體驗的角度來說,我們應(yīng)該首選餓漢模式。我們愿意等待某個程序花較長的時間初始化,卻不喜歡在程序運行時等待太久,給人一種反應(yīng)遲鈍的感覺,所以對于有重量級對象參與的單例模式,筆者推薦使用餓漢模式
在JAVA語言中,String類型就是使用了享元模式。String對象是final類型,對象一旦創(chuàng)建就不可改變
在JAVA中字符串常量都是存在常量池中的,JAVA會確保一個字符串常量在常量池中只有一個拷貝
String a="abc",其中"abc"就是一個字符串常量
享元模式代碼:
public class Test {
public static void main(String[] args) {
String a = "abc";
String b = "abc";
System.out.println(a == b);
}
}
上面的例子中結(jié)果為:true ,這就說明a和b兩個引用都指向了常量池中的同一個字符串常量"abc"
這樣的設(shè)計避免了在創(chuàng)建N多相同對象時所產(chǎn)生的不必要的大量的資源消耗
簡單工廠模式是由一個工廠對象決定創(chuàng)建出哪一種產(chǎn)品類的實例
簡單工廠模式是工廠模式家族中最簡單實用的模式,工廠模式就是用來生成對象的
簡單工廠設(shè)計模式作用:降低耦合
簡單工廠模式代碼:
//手機標(biāo)準(zhǔn)
interface ICellPhone {
void sendMsg();
}
/* 小米手機 */
class Millet implements ICellPhone {
public void sendMsg() {
}
}
/* 華為手機 */
class Huawei implements ICellPhone {
public void sendMsg() {
}
}
/* 手機工廠 */
class Factory {
public static ICellPhone getInstance(String type){
ICellPhone phone = null;
if("millet".equalsIgnoreCase(type)){
phone = new Millet();
}else if("huawei".equalsIgnoreCase(type)){
phone = new Huawei();
}
return phone;
}
}
public class FactoryDemo {
public static void main(String[] args) {
ICellPhone p = Factory.getInstance("millet");
}
}
如果直接使用了被調(diào)用者對象,而且又有可能會變化,那這個代碼的可擴展性和柔韌性就不是很強基于這樣的問題,所有我們就提出把客戶端(調(diào)用者)不直接跟要調(diào)用的對象產(chǎn)生依賴關(guān)系,這樣在擴展性和柔韌性會好一些,加入中間人,來引入工廠模式調(diào)控,單獨聲明一個工廠類,屬于被調(diào)用者這一邊,簡單工廠類只負(fù)責(zé)產(chǎn)生對象
抽象工廠模式與簡單工廠模式的區(qū)別:
(1)抽象工廠模式是簡單工廠方法模式的升級版本,它用來創(chuàng)建一組相關(guān)或者相互依賴的對象。它與簡單工廠方法模式的區(qū)別就在于,簡單工廠方法模式針對的是一個產(chǎn)品等級結(jié)構(gòu);而抽象工廠模式則是針對的多個產(chǎn)品等級結(jié)構(gòu)
(2)在編程中,通常一個產(chǎn)品結(jié)構(gòu),表現(xiàn)為一個接口或者抽象類,也就是說,簡單工廠方法模式提供的所有產(chǎn)品都是衍生自同一個2接口或抽象類,而抽象工廠模式所提供的產(chǎn)品則是衍生自不同的接口或抽象類
(3)在抽象工廠模式中,有一個產(chǎn)品族的概念:所謂的產(chǎn)品族,是指位于不同產(chǎn)品等級結(jié)構(gòu)中功能相關(guān)聯(lián)的產(chǎn)品組成的家族。抽象工廠模式所提供的一系列產(chǎn)品就組成一個產(chǎn)品族;而工廠方法提供的一系列產(chǎn)品稱為一個等級結(jié)構(gòu)
抽象工廠模式代碼:
interface IProduct1 {
public void show();
}
interface IProduct2 {
public void show();
}
class Product1 implements IProduct1 {
public void show() {
System.out.println("這是1型產(chǎn)品");
}
}
class Product2 implements IProduct2 {
public void show() {
System.out.println("這是2型產(chǎn)品");
}
}
interface IFactory {
public IProduct1 createProduct1();
public IProduct2 createProduct2();
}
class Factory implements IFactory{
public IProduct1 createProduct1() {
return new Product1();
}
public IProduct2 createProduct2() {
return new Product2();
}
}
public class Client {
public static void main(String[] args){
IFactory factory = new Factory();
factory.createProduct1().show();
factory.createProduct2().show();
}
}
抽象工廠模式的優(yōu)點:
抽象工廠模式除了具有簡單工廠方法模式的優(yōu)點外,最主要的優(yōu)點就是可以在類的內(nèi)部對產(chǎn)品族進行約束,所謂的產(chǎn)品族,一般或多或少的都存在一定的關(guān)聯(lián),抽象工廠模式就可以在類內(nèi)部對產(chǎn)品族的關(guān)聯(lián)關(guān)系進行定義和描述,而不必專門引入一個新的類來進行管理
抽象工廠模式的缺點:
產(chǎn)品族的擴展將是一件十分費力的事情,假如產(chǎn)品族中需要增加一個新的產(chǎn)品,則幾乎所有的工廠類都需要進行修改,所以使用抽象工廠模式時,對產(chǎn)品等級結(jié)構(gòu)的劃分是非常重要的
適用場景
當(dāng)需要創(chuàng)建的對象是一系列相互關(guān)聯(lián)或相互依賴的產(chǎn)品族時,便可以使用抽象工廠模式,說的更明白一點,就是一個繼承體系中,如果存在著多個等級結(jié)構(gòu)(即存在著多個抽象類),并且分屬各個等級結(jié)構(gòu)中的實現(xiàn)類之間存在著一定的關(guān)聯(lián)或者約束,就可以使用抽象工廠模式,假如各個等級結(jié)構(gòu)中的實現(xiàn)類之間不存在關(guān)聯(lián)或約束,則使用多個獨立的工廠來對產(chǎn)品進行創(chuàng)建,則更合適一點
裝飾模式在不鏈接其結(jié)構(gòu)的情況下向現(xiàn)有對象添加新功能,它是一種結(jié)構(gòu)型模式,因為它充當(dāng)現(xiàn)有類的包裝器
裝飾模式創(chuàng)建一個裝飾器類來包裝原始類并提供其他功能
裝飾模式代碼:
interface Printer {
void print();
}
class PaperPrinter implements Printer {
@Override
public void print() {
System.out.println("Paper Printer");
}
}
class PlasticPrinter implements Printer {
@Override
public void print() {
System.out.println("Plastic Printer");
}
}
abstract class PrinterDecorator implements Printer {
protected Printer decoratedPrinter;
public PrinterDecorator(Printer d){
this.decoratedPrinter = d;
}
public void print(){
decoratedPrinter.print();
}
}
class Printer3D extends PrinterDecorator {
public Printer3D(Printer decoratedShape) {
super(decoratedShape);
}
@Override
public void print() {
System.out.println("3D.");
decoratedPrinter.print();
}
}
public class Main {
public static void main(String[] args) {
Printer plasticPrinter = new PlasticPrinter();
Printer plastic3DPrinter = new Printer3D(new PlasticPrinter());
Printer paper3DPrinter = new Printer3D(new PaperPrinter());
plasticPrinter.print();
plastic3DPrinter.print();
paper3DPrinter.print();
}
}
## 6.JAVA設(shè)計模式 - 觀察者設(shè)計模式
定義對象間一種一對多的依賴關(guān)系,使得當(dāng)每一個對象改變狀態(tài),則所有依賴于它的對象都會得到通知并自動更新
觀察者模式中,包括以下四個角色:
(1)被觀察者:類中有一個用來存放觀察者對象的Vector容器(之所以使用Vector而不使用List,是因為多線程操作時,Vector在是安全的,而List則是不安全的),這個Vector容器是被觀察者類的核心,另外還有三個方法:attach方法是向這個容器中添加觀察者對象;detach方法是從容器中移除觀察者對象;notify方法是依次調(diào)用觀察者對象的對應(yīng)方法。這個角色可以是接口,也可以是抽象類或者具體的類,因為很多情況下會與其他的模式混用,所以使用抽象類的情況比較多
(2)觀察者:觀察者角色一般是一個接口,它只有一個update方法,在被觀察者狀態(tài)發(fā)生變化時,這個方法就會被觸發(fā)調(diào)用
(3)具體的被觀察者:使用這個角色是為了便于擴展,可以在此角色中定義具體的業(yè)務(wù)邏輯
(4)具體的觀察者:觀察者接口的具體實現(xiàn),在這個角色中,將定義被觀察者對象狀態(tài)發(fā)生變化時所要處理的邏輯
觀察者模式代碼實現(xiàn):
abstract class Subject {
private Vector obs = new Vector();
public void addObserver(Observer obs){
this.obs.add(obs);
}
public void delObserver(Observer obs){
this.obs.remove(obs);
}
protected void notifyObserver(){
for(Observer o: obs){
o.update();
}
}
public abstract void doSomething();
}
class ConcreteSubject extends Subject {
public void doSomething(){
System.out.println("被觀察者事件");
this.notifyObserver();
}
}
interface Observer {
public void update();
}
class ConcreteObserver1 implements Observer {
public void update() {
System.out.println("觀察者1收到信息進行處理");
}
}
class ConcreteObserver2 implements Observer {
public void update() {
System.out.println("觀察者2收到信息進行處理");
}
}
public class Client {
public static void main(String[] args){
Subject sub = new ConcreteSubject();
sub.addObserver(new ConcreteObserver1()); //添加觀察者1
sub.addObserver(new ConcreteObserver2()); //添加觀察者2
sub.doSomething();
}
}
觀察者模式的優(yōu)點:觀察者與被觀察者之間是屬于輕度的關(guān)聯(lián)關(guān)系,并且是抽象耦合的,這樣對于兩者來說都比較容易進行擴展,觀察者模式是一種常用的觸發(fā)機制,它形成一條觸發(fā)鏈,依次對各個觀察者的方法進行處理,但同時,這也算是觀察者模式一個缺點,由于是鏈?zhǔn)接|發(fā),當(dāng)觀察者比較多的時候,性能問題是比較令人擔(dān)憂的。并且,在鏈?zhǔn)浇Y(jié)構(gòu)中,比較容易出現(xiàn)循環(huán)引用的錯誤,造成系統(tǒng)假死
在JAVA設(shè)計模式中,適配器模式作為兩個不兼容接口之間的橋梁
通過使用適配器模式,可以統(tǒng)一兩個不兼容的接口
適配器設(shè)計模式代碼:
//適配器模式
public class Shipeiqi{
public static void main(String [] args){
ModificationWindow i = new ModificationWindow();
i.close();
}
}
//定義一個接口
interface IWindow{
void man();//只聲明方法,
void min();//只聲明方法
void close();//只聲明方法,
}
//定義一個抽象實現(xiàn)
abstract class MyWindow implements IWindow{
public void man(){};
public void min(){};
public void close(){};
}
//定義一個類繼承接口
class ModificationWindow extends MyWindow{
public void close(){
System.out.println("我將實現(xiàn)關(guān)閉功能");
}
}
靜態(tài)代理設(shè)計模式:如生活當(dāng)中的代理,代駕,代購,待產(chǎn)...
代理模式(Proxy):為其他對象提供一種代理以控制對這個對象的訪問
代理模式,白了就是“真實對象”的代表,在訪問對象時引入一定程度的間接性,因為這種間接性可能附和多種用途(權(quán)限的控制、對象的訪問、遠程的代理)
代理類第一先要實現(xiàn)接口,第二要維護一個代理的對象,代理對象也是通過主題接口聲明的,再通過構(gòu)造方法或者get,set傳值,這就是靜態(tài)代理
靜態(tài)代理模式代碼:
//靜態(tài)代理設(shè)計模式
//聲明一個類
public class Jingtai{
//主方法
public static void main(String [] args){
Person p = new Person("老王 ");//實例化Person對象
//創(chuàng)建代理對象,并把被代理對象傳進來,p傳給了Matchmaker類中的target
Matchmaker m = new Matchmaker(p); //需要一個代理對象,把被代理對象傳過來
m.miai();//真正執(zhí)行調(diào)用的是
}
}
//定義一個接口 主題接口
interface Subject{
public void miai();//抽象方法
}
//代理相當(dāng)于代理接口的方法
//定義實現(xiàn)一個接口,相當(dāng)于被代理類
class Person implements Subject{
private String name;//定義私有的屬性
//一參構(gòu)造方法
public Person(String name){
this.name = name;
}
//定義實現(xiàn)方法
public void miai(){
System.out.println(name+"正在相親中...");//輸出
}
}
//定義一個代理類,代理的是過程,實現(xiàn)以后,是為了實現(xiàn)方法,需要重寫方法
class Matchmaker implements Subject{
private Subject target;//要代理的目標(biāo)對象,通過定義一個代理的對象實現(xiàn)的接口,代理一個對象或者說一個屬性,
//可以用構(gòu)造方法傳值,也可以用get,set方法傳值
//構(gòu)造方法傳值
public Matchmaker(Subject target){
this.target = target;
}
//相親之前要做的事情,封裝起來
private void before(){
System.out.println("為代理人,匹配如意郎君");
}
//相親之后要做的事情
private void after(){
System.out.println("本次相親結(jié)束.");
}
//需要重寫方法,相親的方法
public void miai(){
before();//調(diào)用相親之前要做的事情
target.miai();//真正執(zhí)行相親的方法,調(diào)用需要目標(biāo)對象
after();//相親之后要做的事情
}
}
迭代器模式以順序方式訪問集合對象的元素
迭代器模式代碼:
interface Iterator {
public boolean hasNext();
public Object next();
}
class LetterBag {
public String names[] = {"R" , "J" ,"A" , "L"};
public Iterator getIterator() {
return new NameIterator();
}
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 Main {
public static void main(String[] args) {
LetterBag bag = new LetterBag();
for(Iterator iter = bag.getIterator(); iter.hasNext();){
String name = (String)iter.next();
System.out.println("Name : " + name);
}
}
}
迭代器模式的優(yōu)點:
簡化了遍歷方式,對于對象集合的遍歷,還是比較麻煩的,對于數(shù)組或者有序列表,可以通過下坐標(biāo)來取得,但用戶需要在對集合了解很清楚的前提下,自行遍歷對象,但是對于hash表來說,用戶遍歷起來就比較麻煩了,而引入了迭代器方法后,用戶用起來就簡單的多了,可以提供多種遍歷方式,比如說對有序列表,我們可以根據(jù)需要提供正序遍歷,倒序遍歷兩種迭代器,用戶用起來只需要得到我們實現(xiàn)好的迭代器,就可以方便的對集合進行遍歷了,封裝性良好,用戶只需要得到迭代器就可以遍歷,而對于遍歷算法則不用去關(guān)心
迭代器模式的缺點:
對于比較簡單的遍歷(像數(shù)組或者有序列表),使用迭代器方式遍歷較為繁瑣,像ArrayList,寧可愿意使用for循環(huán)和get方法來遍歷集合
生產(chǎn)者與消費者模式代碼:
package cn.sc;
/**
*生產(chǎn)者與消費者應(yīng)用案例
*sleep與wait區(qū)別
*sleep讓當(dāng)前的線程進入休眠狀態(tài),讓出cpu,讓其他線程執(zhí)行
*如果用同步的話,有對象鎖的時候,是不會釋放的,只能等待此線程使用完,才可以使用
*wait會釋放對象鎖,必須等待其他線程喚醒
*@author JEEP-711
*
*/
public class ScXf {
public static void main(String[] args) {
Phones p = new Phones(null, null);//創(chuàng)建Phones對象
PhoneSc s = new PhoneSc(p);//創(chuàng)建PhoneSc對象
PhoneXf x = new PhoneXf(p);//創(chuàng)建PhoneXf對象
new Thread(s).start();//啟動生產(chǎn)者線程
new Thread(x).start();//啟動消費者線程
}
}
/**
* 手機生產(chǎn)者,單獨的生產(chǎn)者,實現(xiàn)Runnable接口
* @author JEEP-711
*
*/
class PhoneSc implements Runnable{
private Phones phones;
public PhoneSc(Phones phones){
this.phones = phones;
}
@Override
public void run() {
//不斷地生產(chǎn)20份,生產(chǎn)的過程
for (int i = 0; i < 50; i++) {
if(i%2==0){
phones.set("金立手機", "金立手機,中國造!");
}else{
phones.set("小米手機", "小米手機,為發(fā)燒而生!");
}
}
}
}
/**
*手機消費者,顧客
*@author JEEP-711
*
*/
class PhoneXf implements Runnable{
private Phones phones;
public PhoneXf(Phones phones){
this.phones = phones;
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
phones.get();//調(diào)用消費產(chǎn)品方法
}
}
}
/**
*產(chǎn)品的對象,生產(chǎn)的手機
*@author JEEP-711
*
*/
class Phones{
@Override
public String toString() {
return "Phones [name=" + name + ", content=" + content + "]";
}
private String name;
private String content;
/**true表示可以生產(chǎn),false表示可以消費
*作為標(biāo)記,如何flag等于true表示可以生產(chǎn),如何flag等于false表示不可生產(chǎn)
*如果flag等于false表示可以消費狀態(tài),可以取走,flag等于true表示不能取走
*解決重復(fù)值得問題
*/
private boolean flag = true;//表示可以生產(chǎn),false表示可以消費
//構(gòu)造方法
public Phones(String name, String content) {
super();
this.name = name;
this.content = content;
}
//取得名稱方法
public String getName() {
return name;
}
//設(shè)置名稱方法
public void setName(String name) {
this.name = name;
}
//取得內(nèi)容方法
public String getContent() {
return content;
}
//設(shè)置內(nèi)容方法
public void setContent(String content) {
this.content = content;
}
/**
*通過同步,解決了取值錯誤問題
*@param name
*@param content
*/
//生產(chǎn)制造同步方法
public synchronized void set(String name, String content){
if(!flag){
try {
//調(diào)用該方法,當(dāng)前線程進入等待池等待狀態(tài),沒有指定時間,
//需要其他線程喚醒,釋放對象鎖,讓出cpu
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.setName(name);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setContent(content);
flag = false;//表示可以消費,取走
this.notify();//喚醒在該監(jiān)視器上的一個線程
}
//消費產(chǎn)品同步取值方法
public synchronized void get(){
if(flag){
try {
//調(diào)用該方法,當(dāng)前線程進入等待池等待狀態(tài),沒有指定時間,
//需要其他線程喚醒,釋放對象鎖,讓出cpu
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getName()+":"+this.getContent());
flag = true;
this.notify();
}
}
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: