Köprü Kalıbı'nı gerçek dünya uygulamasında kullanan biri oldu mu? Öyleyse, nasıl kullandınız? Benim mi, yoksa karışıma biraz bağımlılık enjeksiyonu ile sadece Adaptör Deseni mi? Gerçekten kendi modelini hak ediyor mu?
Köprü Kalıbı'nı gerçek dünya uygulamasında kullanan biri oldu mu? Öyleyse, nasıl kullandınız? Benim mi, yoksa karışıma biraz bağımlılık enjeksiyonu ile sadece Adaptör Deseni mi? Gerçekten kendi modelini hak ediyor mu?
Yanıtlar:
Köprü deseninin klasik bir örneği, bir UI ortamındaki şekillerin tanımında kullanılır (bkz. Köprü modeli Wikipedia girişi ). Köprü desen bir olan kompozit ait Şablon ve Strateji desenleri.
Köprü desenindeki Adaptör deseninin bazı yönleri ortak bir görünümdür. Ancak, bu makaleden alıntı yapmak için :
İlk bakışta, Köprü deseni, bir sınıfın bir tür arabirimi diğerine dönüştürmek için kullanıldığı Adaptör desenine çok benziyor. Ancak, Bağdaştırıcı modelinin amacı, bir veya daha fazla sınıfın arabiriminin belirli bir sınıfla aynı görünmesini sağlamaktır. Bridge deseni, bir sınıfın arabirimini uygulamasından ayırmak üzere tasarlanmıştır, böylece istemci kodunu değiştirmeden uygulamayı değiştirebilir veya değiştirebilirsiniz.
Federico ve John'un cevaplarının bir kombinasyonu var .
Ne zaman:
----Shape---
/ \
Rectangle Circle
/ \ / \
BlueRectangle RedRectangle BlueCircle RedCircle
Refactor:
----Shape--- Color
/ \ / \
Rectangle(Color) Circle(Color) Blue Red
Köprü deseni eski tavsiyenin bir uygulamasıdır, "miras yerine kompozisyonu tercih et". Farklı zamanları birbirleriyle dikey şekilde alt sınıflara ayırmanız gerektiğinde kullanışlı olur. Diyelim ki renkli şekillerden oluşan bir hiyerarşi uygulamalısınız. Shape'yi Rectangle ve Circle ile alt sınıflara ayırmazsınız ve daha sonra Rectangle'ı RedRectangle, BlueRectangle ve GreenRectangle ile alt sınıfla ve aynı Circle için yapmazsınız, değil mi? Her Şekli söylemek tercih ediyorum sahiptir Renk ve renk hiyerarşisi uygulamak ve bu Köprüsü Formasyonu oluştu. Bir "renk hiyerarşisi" uygulamam ama fikri anladınız ...
Ne zaman:
A
/ \
Aa Ab
/ \ / \
Aa1 Aa2 Ab1 Ab2
Refactor:
A N
/ \ / \
Aa(N) Ab(N) 1 2
Bağdaştırıcı ve Köprü kesinlikle birbiriyle ilişkilidir ve ayrım çok incedir. Bu kalıplardan birini kullandıklarını düşünen bazı insanlar aslında diğer kalıpları kullanıyor olabilirler.
Gördüğüm açıklama, zaten var olan bazı uyumsuz sınıfların arayüzlerini birleştirmeye çalıştığınızda Bağdaştırıcının kullanıldığı . Bağdaştırıcı, eski olarak kabul edilebilecek uygulamalara bir tür çevirmen işlevi görür .
Köprü deseni ise yeşil alan olması muhtemel kodlar için kullanılır. Bridge'i, değişmesi gereken bir uygulama için soyut bir arabirim sağlayacak şekilde tasarlıyorsunuz, ancak bu uygulama sınıflarının arabirimini de tanımlıyorsunuz.
Aygıt sürücüleri, Bridge tarafından sıkça atıfta bulunulan bir örnektir, ancak aygıt satıcıları için arabirim spesifikasyonunu tanımlarsanız bunun bir Köprü olduğunu söyleyebilirim, ancak mevcut aygıt sürücülerini alıyorsanız ve birleşik bir arayüz sağlar.
Yani kod olarak, iki örüntü birbirine çok benzer. İş açısından farklılar.
Ayrıca bkz. Http://c2.com/cgi/wiki?BridgePattern
Deneyimlerime göre, Bridge oldukça sık tekrar eden bir modeldir, çünkü etki alanında iki dikey boyut olduğunda çözüm budur . Örneğin şekiller ve çizim yöntemleri, davranışlar ve platformlar, dosya formatları ve serileştiriciler vb.
Ve bir tavsiye: tasarım kalıplarını daima uygulama perspektifinden değil , kavramsal perspektiften düşünün . Doğru bakış açısından, Bridge, Adaptör ile karıştırılamaz, çünkü farklı bir sorunu çözerler ve kompozisyon, kendi uğruna değil, dikey endişelerin ayrı ayrı ele alınmasına izin verdiği için mirastan daha üstündür.
Amacı Köprüsü ve Adaptörü farklıdır ve ayrı ayrı her iki desenleri gerekir.
Köprü deseni:
Bridge desenini şu durumlarda kullanın:
@ John Sonmez yanıtı, sınıf hiyerarşisini azaltmada köprü modelinin etkinliğini açıkça göstermektedir.
Kod örneği ile köprü deseni hakkında daha iyi bilgi edinmek için aşağıdaki dokümantasyon bağlantısına bakabilirsiniz.
Adaptör deseni :
Temel farklılıklar:
UML diyagramı ve çalışma kodu ile ilgili SE sorusu:
Köprü deseni ve Adaptör deseni arasındaki fark
Yararlı makaleler:
kaynak yapımı köprü desen makale
kaynak yapımı adaptör deseni makalesi
journaldev köprü desen makalesi
DÜZENLE:
Köprü Deseni gerçek dünya örneği (meta.stackoverflow.com önerisine göre, belgeler yayınlanacağı için bu yazıya dahil edilen dokümantasyon sitesi örneği)
Köprü deseni, soyutlamayı uygulamadan ayırır, böylece her ikisi de bağımsız olarak değişebilir. Kalıtımdan çok kompozisyon ile başarılmıştır.
Wikipedia'dan köprü deseni UML'si:
Bu modelde dört bileşene sahipsiniz.
Abstraction
: Bir arayüz tanımlar
RefinedAbstraction
: Soyutlamayı uygular:
Implementor
: Uygulama için bir arayüz tanımlar
ConcreteImplementor
: Implementor arabirimini uygular.
The crux of Bridge pattern :
Kompozisyon kullanan (ve kalıtım olmadan) iki dikey sınıf hiyerarşisi. Soyutlama hiyerarşisi ve Uygulama hiyerarşisi bağımsız olarak değişebilir. Uygulama asla Soyutlama anlamına gelmez. Soyutlama, üye olarak Uygulama arabirimi içerir (kompozisyon aracılığıyla). Bu kompozisyon bir miras hiyerarşisi seviyesini daha azaltır.
Gerçek kelime Kullanım örneği:
Farklı araçların hem manuel hem de otomatik vites sisteminin sürümlerine sahip olmasını sağlayın.
Örnek kod:
/* Implementor interface*/
interface Gear{
void handleGear();
}
/* Concrete Implementor - 1 */
class ManualGear implements Gear{
public void handleGear(){
System.out.println("Manual gear");
}
}
/* Concrete Implementor - 2 */
class AutoGear implements Gear{
public void handleGear(){
System.out.println("Auto gear");
}
}
/* Abstraction (abstract class) */
abstract class Vehicle {
Gear gear;
public Vehicle(Gear gear){
this.gear = gear;
}
abstract void addGear();
}
/* RefinedAbstraction - 1*/
class Car extends Vehicle{
public Car(Gear gear){
super(gear);
// initialize various other Car components to make the car
}
public void addGear(){
System.out.print("Car handles ");
gear.handleGear();
}
}
/* RefinedAbstraction - 2 */
class Truck extends Vehicle{
public Truck(Gear gear){
super(gear);
// initialize various other Truck components to make the car
}
public void addGear(){
System.out.print("Truck handles " );
gear.handleGear();
}
}
/* Client program */
public class BridgeDemo {
public static void main(String args[]){
Gear gear = new ManualGear();
Vehicle vehicle = new Car(gear);
vehicle.addGear();
gear = new AutoGear();
vehicle = new Car(gear);
vehicle.addGear();
gear = new ManualGear();
vehicle = new Truck(gear);
vehicle.addGear();
gear = new AutoGear();
vehicle = new Truck(gear);
vehicle.addGear();
}
}
çıktı:
Car handles Manual gear
Car handles Auto gear
Truck handles Manual gear
Truck handles Auto gear
Açıklama:
Vehicle
bir soyutlamadır. Car
ve Truck
iki somut uygulamasıdır Vehicle
.Vehicle
soyut bir yöntem tanımlar: addGear()
.Gear
implementor arayüzüManualGear
ve AutoGear
iki uygulamasıdır Gear
Vehicle
implementor
arabirimi uygulamak yerine arabirim içerir . Compositon
arayüzünün örneği bu modelin temelidir: Soyutlamanın ve uygulamanın bağımsız olarak değişmesine izin verir. Car
ve Truck
soyutlama için uygulamayı (yeniden tanımlanmış soyutlama) tanımlayın addGear()
:: İçerir Gear
- Ya Manual
yaAuto
Köprü deseni için kullanım örnekleri :
Köprü desenini işte kullandım. Ben genellikle PIMPL deyim (uygulama işaretçisi) denir C ++, programlayın. Şöyle görünüyor:
class A
{
public:
void foo()
{
pImpl->foo();
}
private:
Aimpl *pImpl;
};
class Aimpl
{
public:
void foo();
void bar();
};
Bu örnekte class A
arabirimi ve class Aimpl
uygulamayı içerir.
Bu örüntü için bir kullanım, uygulama sınıfının sadece bazı kamu üyelerini ortaya çıkarmaktır, diğerlerini değil. Örnekte sadece Aimpl::foo()
genel arayüzü üzerinden çağrılabilir A
, ancakAimpl::bar()
Başka bir avantaj, Aimpl
kullanıcıları tarafından dahil edilmesi gerekmeyen ayrı bir başlık dosyasında tanımlayabilmenizdir A
. Yapmanız gereken tek şey, daha Aimpl
önce A
tanımlanmış bir ileri bildirim kullanmak ve başvuruda bulunan tüm üye işlevlerin tanımlarını pImpl
.cpp dosyasına taşımaktır . Bu, Aimpl
üstbilgiyi gizli tutma ve derleme süresini azaltma olanağı sağlar.
Koda şekil örneği koymak için:
#include<iostream>
#include<string>
#include<cstdlib>
using namespace std;
class IColor
{
public:
virtual string Color() = 0;
};
class RedColor: public IColor
{
public:
string Color()
{
return "of Red Color";
}
};
class BlueColor: public IColor
{
public:
string Color()
{
return "of Blue Color";
}
};
class IShape
{
public:
virtual string Draw() = 0;
};
class Circle: public IShape
{
IColor* impl;
public:
Circle(IColor *obj):impl(obj){}
string Draw()
{
return "Drawn a Circle "+ impl->Color();
}
};
class Square: public IShape
{
IColor* impl;
public:
Square(IColor *obj):impl(obj){}
string Draw()
{
return "Drawn a Square "+ impl->Color();;
}
};
int main()
{
IColor* red = new RedColor();
IColor* blue = new BlueColor();
IShape* sq = new Square(red);
IShape* cr = new Circle(blue);
cout<<"\n"<<sq->Draw();
cout<<"\n"<<cr->Draw();
delete red;
delete blue;
return 1;
}
Çıktı:
Drawn a Square of Red Color
Drawn a Circle of Blue Color
Permütasyonlar nedeniyle alt sınıfların patlamasına yol açmadan sisteme yeni renklerin ve şekillerin eklenebilme kolaylığına dikkat edin.
Muhasebe, sözleşme, talepler gibi farklı görevleri yöneten bir iş akışı uygulaması geliştirdiğiniz bir sigorta şirketi için çalışıyorsunuz. Bu soyutlama. Uygulama tarafında, farklı kaynaklardan görevler oluşturabilmeniz gerekir: e-posta, faks, e-mesajlaşma.
Tasarımınıza şu sınıflarla başlıyorsunuz:
public class Task {...}
public class AccountingTask : Task {...}
public class ContractTask : Task {...}
public class ClaimTask : Task {...}
Şimdi, her kaynak belirli bir şekilde ele alınması gerektiğinden, her görev türünü uzmanlaştırmaya karar verdiniz:
public class EmailAccountingTask : AccountingTask {...}
public class FaxAccountingTask : AccountingTask {...}
public class EmessagingAccountingTask : AccountingTask {...}
public class EmailContractTask : ContractTask {...}
public class FaxContractTask : ContractTask {...}
public class EmessagingContractTask : ContractTask {...}
public class EmailClaimTask : ClaimTask {...}
public class FaxClaimTask : ClaimTask {...}
public class EmessagingClaimTask : ClaimTask {...}
13 ders ile sonuçlanırsınız. Görev türü veya kaynak türü eklemek zorlaşır. Köprü kalıbını kullanmak, görevi (soyutlama) kaynaktan (bir uygulama sorunu olan) ayırarak bakımı daha kolay bir şey üretir:
// Source
public class Source {
public string GetSender();
public string GetMessage();
public string GetContractReference();
(...)
}
public class EmailSource : Source {...}
public class FaxSource : Source {...}
public class EmessagingSource : Source {...}
// Task
public class Task {
public Task(Source source);
(...)
}
public class AccountingTask : Task {...}
public class ContractTask : Task {...}
public class ClaimTask : Task {...}
Görev türü veya kaynak eklemek artık çok daha kolay.
Not: Çoğu geliştirici bu sorunu çözmek için 13 sınıf hiyerarşisini önceden oluşturmaz. Ancak, gerçek hayatta, kaynak ve görev türlerinin sayısını önceden bilmiyor olabilirsiniz; yalnızca bir kaynağınız ve iki görev türünüz varsa, muhtemelen Görevi Kaynaktan ayırmazsınız. Ardından, yeni kaynaklar ve görev türleri eklendikçe genel karmaşıklık artar. Bir noktada, yeniden düzenleyecek ve çoğu zaman köprü benzeri bir çözüm elde edeceksiniz.
Bridge design pattern we can easily understand helping of service and dao layer.
Dao layer -> create common interface for dao layer ->
public interface Dao<T>{
void save(T t);
}
public class AccountDao<Account> implement Dao<Account>{
public void save(Account){
}
}
public LoginDao<Login> implement Dao<Login>{
public void save(Login){
}
}
Service Layer ->
1) interface
public interface BasicService<T>{
void save(T t);
}
concrete implementation of service -
Account service -
public class AccountService<Account> implement BasicService<Account>{
private Dao<Account> accountDao;
public AccountService(AccountDao dao){
this.accountDao=dao;
}
public void save(Account){
accountDao.save(Account);
}
}
login service-
public class LoginService<Login> implement BasicService<Login>{
private Dao<Login> loginDao;
public AccountService(LoginDao dao){
this.loginDao=dao;
}
public void save(Login){
loginDao.save(login);
}
}
public class BridgePattenDemo{
public static void main(String[] str){
BasicService<Account> aService=new AccountService(new AccountDao<Account>());
Account ac=new Account();
aService.save(ac);
}
}
}