最近見て《Think In JAVA》
抽象クラスとインタフェースの組み合わせは、いくつかのデザインパターンの問題に言及する場合、連続して説明しています、。これらのデザインパターンはまた私JAVA内のデータ構造の意味をより良く理解させるん。今日まで少しきちんと自分の理解の組み合わせに関する書籍、これらのデザインパターンがあります。
Strategyパターン|戦略パターン
ここでは言及する必要があり向上转化
、この概念を。継承とインターフェイスは、この概念を言及しています。
変換は上向き継承サブクラスは親クラスをアップコンバートできることを意味します。たとえば、クラスのインストゥルメント、およびそのサブクラスのフルートのものがあります。プレイサブクラス書き換えは親クラスよりも優先されます。
class Instrument{
public void play(){
...
//play instrument
}
}
class Flute extends Instrument{
public void play(){
...
//play flute
}
}
場合は、その後方法がある楽器のパラメータを受信して再生するために必要、あなたはより多くのオーバーロード記述する必要はありません方法は、1つの方法だけのインストゥルメントクラスの受信を異なる楽器の様々なを受けます。
public void play(Instrument instrument){
instrument.play();
}
この方法ではフルートのオブジェクトを通過した、コールはメソッドでフルートを演奏することになります。つまり、コンバージョンまで動的結合の最も単純な例の一つです。
実際には、インタフェースは、同じ理由であるが、インターフェースは、変換された上向きの多様を可能にします。換言すれば、JAVAのみ継承が、インターフェイスは複数を実装します。したがってだけ上方JAVA継承路変換、上方及びインターフェース変換パスは一意ではありません。
次のステップは、ポリシーモードの話されています。
戦略モードの概念を次のように:
ことができるカプセル化されたアルゴリズムのセット定義する特定の動作を実行するために交換され
、それぞれ異なる動作を行うアルゴリズムのパッケージ化セットを定義します。実際の動作では、これらのアルゴリズムは、動的に異なるシナリオのニーズを満たすように切り替えることができます
Strategyパターンの使用シナリオは以下のとおりです。
別の形式にファイルを保存します
ソートアルゴリズムの多様性を達成するために
ファイル圧縮の多様性を達成するために
すなわち、戦略パターンを切り替えることにより動作において、それぞれ異なるクラスに同じ仕事をするために、異なる方法で、ポリシーモデルに、および互いにコードのセットです。
これは見つけるためのインターネットからの戦略パターンのUML図です。
JAVAストラテジーパターンは、抽象クラスとインタフェースの包括的なアプリケーションを継承しています。戦略モードでは、我々はできる「オープンインタフェース」によると「具体的な戦略」の様々な設計、その後、あなたが呼び出すときだけ「オープンインタフェース」を入力する必要があり、実行されている特定のプログラムの具体的な実装「オープンインタフェース」に応じて決定されます営業成績。
上記設計パターンコードは次の通りであります:
//接口
public interface Strategy {
public int doOperation(int num1, int num2);
}
//接口实现类
public class OperationAdd implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
public class OperationSubstract implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
public class OperationMultiply implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}
//上下文
public class Context {
private Strategy strategy;
public Context(Strategy strategy){
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2){
return strategy.doOperation(num1, num2);
}
}
//具体调用
public class StrategyPatternDemo {
public static void main(String[] args) {
Context context = new Context(new OperationAdd());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationSubstract());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationMultiply());
System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
}
}
Strategyパターンを使用し、戦略パターンのギャップを使用していません
私は具体的な例としては、Strategyパターンの使用を説明するためにギャップが戦略パターンを使用していないことを繰り返してみましょう。
私たちは、ファイル圧縮があると、圧縮されたファイルは、そのようなので、上のジップ、RARやなどのアルゴリズム、さまざまなを持っています。圧縮プログラムを選択すると、オペレーティングシステムと実際の性能パラメータのアルゴリズムに従って圧縮動作を行うことができます。私たちは、特定のアルゴリズムに置かCompressionPreferenceクラスを選択するには、この機能を前提としています。
実際には、これらのクライアントのためにそれは透明です。言い換えれば、クライアントは圧縮したファイルをアップロードするには、顧客が必要となる圧縮機能があることを知っています。このようなシナリオの下では、サーバが唯一の実装を公開することなく、圧縮・インターフェースを提供する必要があります。
コードは以下の通りであります:
//选择压缩方法类,根据具体情况返回压缩的方法
public class CompressionPreference{
public static CompressionStrategy getPreferedStrategy(){
//根据系统的情况或是用户的选择返回具体的压缩算法
}
}
//压缩策略接口
public interface CompressionStrategy{
void compress(List<File> files);
}
//压缩策略的具体实现
public class ZipCompressionStrategy implements CompressionStrategy{
@Override
public void compress(List<File> files){
//zip 压缩
}
}
public class RarCompressionStrategy implements CompressionStrategy{
@Override
public void compress(List<File> files){
//RAR 压缩
}
}
public class CompressionContext{
private CompressionStrategy strategy;
//这里根据CompressionPreference选择压缩策略
public void setStrategy(CompressionStrategy strategy){this.strategy=strategy);}
public void createArchieve(List<File> files){
strategy.compress(files);
}
}
//客户端调用
public class Client{
public static void main(String[] args) {
CompressionContext ctx = new CompressionContext();
//设置压缩上下文
ctx.setCompressionStrategy(CompressionPreference.getPreferedStrategy());
ctx.createArchive(fileList);
}
}
あなたは新しいアルゴリズムを追加する必要がある場合は、このデザインに従うことにより、唯一の我々はCompressionStrategy実装クラスを追加するだけでなく、CompressionPreferenceの方法を変更する必要があります。これは、クライアントの呼び出しには影響しません。
アルゴリズムのパッケージがなければ、直接のクライアントが言って呼び出すことができます。
一方では、圧縮アルゴリズムを達成するため、あらゆる種類にさらされ、
一方、また間違ったコール引き起こす可能性を増加させました。
また、新しい圧縮アルゴリズムが増加した後、クライアントは、これは彼らの呼び出しを調整するために、これらの事を知っている必要はありません知っている必要があります。このコードの保守は本当に悪いです。
Adapterパターン|アダプターデザインパターン
定義:
他に、クラスのインタフェースを変換し 、クライアントが期待するインターフェイス。アダプタは、クラスが一緒に作業することができますことはないそうでした。理由は、互換性のないインタフェースの
インタフェースアダプタを介して相互に互換性のない2つの変換されたの。
アダプタモード:あなたが使用したいが、既存のクラスであるが、符号化プロセスに拡張するためのオープン従うべきと彼は、インターフェイスの要件を満たしていない、原則の変更のため閉鎖、それは元のクラスを変更することはできません、これはあなたは、アダプタモード、自分のニーズに合わせたクラスの元のフォームを使用する必要があります。オブジェクトアダプタクラスアダプタおよびアダプタの2種類があります。
モードよりもアダプタモードは、より良い戦略のいくつかを理解しています。本はアダプタモードを説明すると、実際には、インターフェイスにプログラミングを追加する方法を説明します。アダプタモードは、定義により、インタフェース呼び出しによって変換することができるインタフェースアダプタのクラスによって継承されていない、それは、2つのであっても、互換性のないインターフェースとの間のブリッジとして機能します。
用語の本来の意味からは、インタフェースアダプタコンバータを指し、生活の中で、最も一般的なインターフェースコンバータは、それをスレッド充電あなたの携帯電話です!標準的な220Vソケット出力電圧からヘッドを充電(内部)安全な充電電圧に変換することができます。電話はすべてのUSBポートを含むチャージラインで充電することができるようにし、USB充電ポートは、他の側に設けられました。SDカードである他の例として。カメラの友人は一部のコンピュータは、SDカードインタフェースを提供しないことを知って使用してください、あなたは、SDカードリーダー、USBインタフェースを介してコンピュータにリーダーにSDカードが必要です。そして、コンピュータは、SDカードの内容を読み取ることができます。
例から、修飾されたクラスがクラスはインターフェイスと呼ばれるようなインタフェースを実現することができる継承シーンアダプタが適用されません。
ブックアダプタモデルは、1つのプロキシを介して、他の継承を介して行われている2つの方法で実装します。必要がある場合には、二つの方法で本質的に同じである元のクラス、継承によって達成アダプタを達成するために、すべての場合には、実装の一部だけの方法エージェントによって、。特定の状況の具体的な分析。
話よりも、具体例については、以下の、あまりにも抽象的です。
例えば、私は彼が方法を持って、スキャンクラスのスキャナを持って、あなたはすべての継承された読み取り可能なインターフェイスクラスを受け取り、そのデータが読み出された場合のクラスに応じてすることができます。システムは、すでにいくつかのカテゴリがあり、彼らは、スキャナを読み取ることができるが、それらはReadableインタフェースを継承しませんでした。
聖霊は、开闭原则
それがスキャナを読み取ることができるように、私は、これらのクラスにアダプタを追加することができます。このモードでは、新しいスキャナの出現の両方がファイルを読み込むか、既存のファイルシステムを読んで、スキャナの方法を変更する必要はありません。読めるだけでライン上のインタフェースをサポートする必要があります。
public class Scanner{
public void read(Readable material){
material.read();
}
}
public interface Readable{
void read();
}
public class TXT implements Readable{
...
public void read(){
//读取txt文件
}
...
}
public class HTML{
public void toReadableFormat(){
//html文件也可以被读取,但是它并没有继承Readable接口,所以无法被Scanner
识别
}
}
//这里才是适配器模式
public class HTMLAdapter implements Readable{
...
private HTML html;
public HTMLAdapter(HTML html){this.html = html}
public void read(){
html.toReadableFormat();
}
...
}
//这时候两个文件都可以被读取了
public class Test{
public static void main(String[] args){
Scanner s = new Scanner();
s.read(new TXT());
s.read(new HTMLAdapter(new HTML()));
}
}
一つの例は、〜1を来、十分ではありません
メディア、より多様なメディアフォーマット、初期テキストからMP3へ、そしてビデオフォーマットからの発展の歴史の中で。我々は今、システムが新しいメディアカテゴリの再生ファイルをサポートすることができるようにするために、もともとのみMP3形式でファイルを読み込むサポートするシステム、この時間を持っている場合。
別のチームが開発した新しいメディア・ファイルには、独自の開発・インタフェースと実装を持っています。それは、既存のシステムにモジュールを統合する方法?
今回はアダプタモードによってこの問題を解決する必要があります。
この動作は、UMLクラス図によるものです。元のシステムに新しいMediaAdapterアダプタを作成することにより、元のシステムが根本的な変化の基礎を知ることができないように、再生を達成するために、元のplayメソッドを呼び出すために引き続き、元のメディアプレーヤーインタフェースを継承します。
具体的なコードは次のよう:
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) {
//do nothing
}
}
public class Mp4Player implements AdvancedMediaPlayer{
@Override
public void playVlc(String fileName) {
//do nothing
}
@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 AdapterPatternDemo {
public static void main(String[] args) {
MediaPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3", "beyond the horizon.mp3");
MediaPlayer videoPlayer = new MediaAdapter();
videoPlayer.play("vlc", "far far away.vlc");
}
}
ファクトリパターン|工場デザインパターン
そして最後に長い時間のために書くために工場出荷時のパターンに~~~~ああQAQ
工厂模式是为了管理一个接口之下众多实现类。比如最常见的DAO接口。数据库中的表少至10个多可以至百个。在spring框架中,通过IOC和DI实现了这么多读取数据库的接口实现类的管理。那么在没有框架的场景下,如何才可以使上层代码和下层具体的DAO接口解耦呢?这时就需要工厂模式。通过工厂模式获得具体DAO接口。
至于为什么要选择这样的一个工厂模式,而不是直接new一个具体的实现类呢?这里举个例子。比方说,有一个DAO接口,实现该接口的有UserDaoImpl, AccountDaoImpl等。假设有两个类均用到UserDaoImpl。如果在这两个类中均使用new来创建一个新的UserDaoImpl,那么一旦有一天,因为需求变更,需要将UserDaoImpl换成AnotherUserDaoImpl,则需要在两个类中分别修改。那么如果有十个类,甚至一百个类都用到了这个Dao呢?这时候如果我是通过工厂来获得这个Dao,也就只需要在工厂中将返回值从原来的UserDaoImpl变成AnotherUserDaoImpl,并不会影响调用方。
简单工厂模式 | Static Factory Method
下面给一个简单的工厂模式的例子。
interface Dog
{
public void speak ();
}
class Poodle implements Dog
{
public void speak()
{
System.out.println("The poodle says \"arf\"");
}
}
class Rottweiler implements Dog
{
public void speak()
{
System.out.println("The Rottweiler says (in a very deep voice) \"WOOF!\"");
}
}
class SiberianHusky implements Dog
{
public void speak()
{
System.out.println("The husky says \"Dude, what's up?\"");
}
}
class DogFactory
{
public static Dog getDog(String criteria)
{
if ( criteria.equals("small") )
return new Poodle();
else if ( criteria.equals("big") )
return new Rottweiler();
else if ( criteria.equals("working") )
return new SiberianHusky();
return null;
}
}
public class JavaFactoryPatternExample
{
public static void main(String[] args)
{
// create a small dog
Dog dog = DogFactory.getDog("small");
dog.speak();
// create a big dog
dog = DogFactory.getDog("big");
dog.speak();
// create a working dog
dog = DogFactory.getDog("working");
dog.speak();
}
}
在简单的工厂模式中,工厂根据输入的条件返回给一个接口的具体实现。
简单工厂模式有一个问题,就是一旦工厂出现新的产品,就必须修改工厂中获取产品的方法,这有违开闭原则。
而且工厂模式承担的压力过重,可能会导致职责的混乱。最重要的是,简单工厂模式中,获取产品的方法是静态方法,该方法无法通过继承等形式得到扩展。
工厂方法模式 | Factory Method Pattern
这其实是工厂模式的一个简单的升级。考虑一个真实工厂的场景。它的产品Product之下往往还有许多分类,如轴承,轮胎。各个子分类往往也对应着不同的车间,如轴承车间,轮胎车间。
如果还用简单工厂模式返回一个Product,且不说向上转型可能丢失的一些数据,而且工厂的压力也太大了,因为可能要根据不同场景返回上百个不同类型但继承了同一接口的类。这不符合设计原则。
这时候就出现了工厂方法模式。不仅仅对产品抽象,还对工厂抽象。对不同的产品提供不同的工厂,将职责进一步细化,满足SRP(单一职责原则)。同时,因为不需要输入无关的判断数据,也解除了控制耦合。
具体例子有最常见的日志系统。日志系统之下往往针对各个不同的子系统,比如数据库日志子系统,比如文件日志子系统。不同的日志系统对应的日志文件也不同。
这时通过工厂方法模式就可以很好的解决二者之间的关系。
在这张图中还可以继续延伸,比如数据库包括Mysql数据库,Oracle数据库等等。在UML图中也可以继续根据MySqlLog创建MysqlLogFactory。
还有一个具体的例子就是JAVA中的数据库连接。
Connection conn=DriverManager.getConnection("jdbc:microsoft:sqlserver://loc
alhost:1433; DatabaseName=DB;user=sa;password=");
Statement statement=conn.createStatement();
ResultSet rs=statement.executeQuery("select * from UserInfo");
这里通过DriverManager工厂根据输入的信息返回一个对应的连接。
连接中再返回对应的抽象语句statement。
根据工厂中的信息可以知道,这个statement的底层实现必定是一个类似SqlServerStatement的实现。
抽象工厂模式 | Abstract Factory Method
产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。
抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。
在这里的上下文中,抽象工厂之下的子工厂被划分为海尔子工厂,海信子工厂。在海尔自工厂中可以获得海尔冰箱(productA),海尔电视机(productB), 同理,在海信子工厂中,可以获得海信冰箱(productA),海信电视机(productB)。
当然了 在大多数的应用场景下,工厂设计模式已经足够了。
对比1:适配器模式和抽象工厂模式的区别
工厂模式是负责加工的,适配器模式包括工厂模式,比工厂模式又高了一点点,增加了统一的抽象接口定义,方便以后在不同的数据库切换而不用改底层代码
对比2:适配器模式与装饰器模式的区别
适配器与装饰器模式的别名都是包装模式(Wrapper)。
区别
---适配器模式的意义
将一个接口转变成另一个接口,目的是通过改变接口来达到重复使用的目的。
----装饰器模式的意义
不改变被装饰对象的接口,而是保持原有的接口,增强原有对象的功能,或改变原有对象的处理方式而增提高性能。