본문 바로가기
James Cooper - Python Programming with D

The Bridge Pattern

by 자동매매 2023. 3. 23.

13
The Bridge Pattern
At first sight, the Bridge pattern looks much like the Adapter pattern in that
a class is used to convert one kind of interface to another. However, the
intent of the Adapter pattern is to make one or more classes’ interfaces look
the same as the interface of a particular class. The Bridge pattern is
designed to separate a class’s interface from its implementation so that you
can vary or replace the implementation without changing the client code.
Suppose that we have a program that displays a list of products in a
window. The simplest interface for that display is a simple Listbox. But
after a significant number of products have been sold, we may want to
display the products in a table along with their sales figures.
We have just discussed the Adapter pattern, so you might think immediately
of the class-based adapter, where we adapt the interface of the Listbox to
our simpler needs in this display. In simple programs, this works fine, but as
we’ll see below, there are limits to that approach.
Let’s further suppose that we need to produce two kinds of displays from
our product data: a customer view, which is just the list of products we’ve
already mentioned, and an executive view, which also shows the number of
units shipped. We’ll display the product list in an ordinary Listbox and
display the executive view in a Treeview display (see Figure 13-1). These
two displays are the implementations of the display classes.
Figure 13-1 Parts list with Treeview
Now, we want to define a single simple interface that remains the same,
regardless of the type and complexity of the actual implementation classes.
We’ll start by defining an abstract Bridger class:
Click here to view code image
class Bridger(Frame):
def addData(self): pass
This class is so simple that it just receives a List of data and passes it on to
the display classes.
On the other side of the bridge are the implementation classes, which
usually have a more elaborate and somewhat lower-level interface. Here
we’ll have them add the data lines to the display one at a time.
Click here to view code image
class VisList():
def addLines(self): pass
def removeLine(self): pass
Implicit in the definition of these classes is some mechanism for
determining which part of each string is the name of the product and which
part is the quantity shipped. In this simple example, we separate the
quantity from the name with two dashes and parse these apart in a Product
class.
The Bridge between the interface on the left and the implementation on the
right is the ListBridge class, which instantiates one or the other of the list
display classes. Note that it extends the Bridger class for use of the
application program.
Click here to view code image

General bridge between data and any VisList class

class ListBridge(Bridger):
def init(self, frame, vislist):
self.list = vislist
self.list.pack()

adds the list of Products into any VisList

def addData(self, products):
self.list.addLines( products)
In the current example, we use the Bridge class twice: once to display the
Listbox on the left side and once to display the Treeview table on the right
side.
The power and simplicity of the Bridge pattern becomes obvious when you
realize that you can completely change the display by replacing either or
both of the two VisList classes that display the data. You don’t have to
change the Bridge class code: Just give it new VisLists to display. Those
classes can be anything, as long as they implement the simple VisList
methods. In fact, we left the removeLine method empty here because it isn’t
really relevant to this example.
The VisList for Listbox is quite simple:
Click here to view code image

Listbox visual list

class LbVisList(Listbox, VisList):
def init (self, frame ):
super(). init (frame)
def addLines(self, prodlist):
for prod in prodlist:
self.insert(END, prod.name)
The Treeview table on the right is equally simple, except for setting up the
column names and dimensions:
Click here to view code image

Treelist (table) visual list

class TbVisList(Treeview, VisList) :
def init (self, frame ):
super(). init (frame)

set up table columns

self[ "columns" ] = ( "quantity" )
self.column( "#0" , width= 150 , minwidth= 100 , stretch=NO)
self.column( "quantity" , width= 50 , minwidth= 50 , stretch=NO)
self.heading( '#0' , text= 'Part' )
self.heading( 'quantity' , text= 'Qty'
self.index = 0 #row counter

adds the whole list of products to the table

def addLines(self, prodlist):
for prod in prodlist:
self.insert( "" , self.index, text=prod.name,
values=(prod.count))
self.index += 1
Creating the User Interface
Although all the usual grid and pack layout code still applies, creating the
two frames inside a two-member grid is really easy. Coding involves
creating the frame and the VisList, creating the Bridge, and adding the
data:
Click here to view code image
self.vislist = LbVisList(self.lframe)
self.lbridge = ListBridge(self.lframe, self.vislist)
self.lbridge.addData(prod.getProducts())
Similarly, you create the Treeview version of a VisList, add it to another
instance of the Bridge, and add the data:
Click here to view code image
self.rvislist = TbVisList(self.rframe)
self.rlb = ListBridge( self.rframe, self.rvislist)
self.rlb.addData(prod.getProducts())
Extending the Bridge
The power of the Bridge becomes apparent when we need to make some
changes in the way these lists display the data. For example, you might
want to have the products displayed in alphabetical order. You might think
you’d need to either modify or subclass both the list and table classes. This
can quickly get to be a maintenance nightmare, especially if more than two
such displays eventually are needed. Instead, we simply make the changes
in the extended interface class, creating a new sortBridge class from the
parent listBridge class. However, you only need to create a new VisList
that sorts the data and installs it instead of the original LbVislist class.
Click here to view code image

Sorted listbox visual list

class SortVisList(Listbox, VisList):
def init (self, frame ):
super(). init (frame)

sort into alphabetical order

def addLines(self, prodlist):

sort array alphabetically

self.prods = self. sortUpwards ( prodlist)
for prod in self.prods:
self.insert(END, prod.name)
The sorting routine is the same one used in the Swimfactory example, so we
won’t repeat it here. Figure 13-2 shows the resulting sorted display.
Figure 13-2 Sorted VisList
This clearly shows that you can vary the interface without changing the
implementation. The converse is also true. For example, you can create
another type of list display and replace one of the current list displays
without any other program changes, as long as the new list also implements
the visList interface.
In the next example, we have created a Treeview component that
implements the visList interface and replaces the ordinary list without any
change in the public interface to the classes in Figure 13-3.
Figure 13-3 Treeview as left VisList
Note that this simple new VisList is the only change in the code:
Click here to view code image
#Tree VisList for left display
class TbexpVisList(Treeview, VisList) :
def init (self, frame ):
super(). init (frame)
self.column( "#0" , width= 150 , minwidth= 100 ,
stretch=NO)
self.index = 0
def addLines(self, prodlist):
for prod in prodlist:
fline = self.insert( "" , self.index,
text=prod.name)

add count as a leaf

self.insert(fline, 'end' ,
text=prod.count )
self.index += 1
Consequences of the Bridge Pattern
Consequences of the Bridge pattern include the following:

  1. The Bridge pattern is intended to keep the interface to your client
    program constant while enabling you to change the actual kind of class
    you display or use. This can prevent you from recompiling a
    complicated set of user interface modules and require only that you
    recompile the bridge itself and the actual end display class.
  2. You can extend the implementation class and the bridge class
    separately, usually without much interaction with each other.
  3. You can hide implementation details from the client program much
    more easily.
    Programs on GitHub
    In all these samples, be sure to include the data file (products.txt) in the
    same folder as the Python file. Also make sure they are part of the project in
    Vscode or PyCharm.
    BasicBridge.py
    SortBridge.py
    TreeBridge.py
    Products.txt: Data file for Bridge programs

'James Cooper - Python Programming with D' 카테고리의 다른 글

The Adapter Pattern  (0) 2023.03.23
Summary of Creational Patterns  (0) 2023.03.23
The Prototype Pattern  (0) 2023.03.23
The Prototype Pattern  (0) 2023.03.23
The Singleton Pattern  (0) 2023.03.23

댓글