A szoftverfejlesztésben a blackboard minta egy viselkedési minta, ami számítási keretrendszert biztosít a tervezéshez és olyan rendszerek implementálásához, amik nagy és különböző specializált modulokat integráltak, és komplex, nem determinisztikus stratégiát valósít meg.[ 1] [ 2]
Ezt a mintát a HEARSAY-II projekt tagjai alkották, és először hangfelismeréshez használták.[ 1]
Szerkezete
A blackboard modellnek három fő összetevője van:
blackboard (Blackboard) - egy strukturált globális memória, ami a megoldás objektumait tartalmazza
tudásbázis (Knowledge Sources)- magasan specializált modulok a saját reprezentációjukkal együtt
vezérlő összetevők (Control) - selectek, konfigurációk és tudásbázisok futtatása.[ 1]
Implementáció
Első lépés, hogy tervezzük meg a megoldás terét, ami a blackboard struktúra definíciójához vezet. Aztán, a tudásbázist kell elkészíteni. Ez a két lépés nagyon összefüggő.[ 1]
A következő lépés, a vezérlők meghatározása, ami általában komplex ütemezés formájában létezik, ami domain-specifikus heurisztikákat használ, hogy rangsorolja a végrehajtható tudásbázist.[ 1]
System Structure [ 1]
Ismert felhasználásai
beszédfelismerés
jármű azonosítás és követés
protein molekulák struktúrájának azonosítása
szonár jelek elemzése[ 1]
Következmények
A blackboard minta hatékony megoldást biztosít olyan komplex rendszerek tervezéséhez és implementálásához, ahol heterogén modulok dinamikus kombinációjával oldjuk meg a problémát. Ez biztosít olyan nem funkcionális tulajdonságokat mint:
újrafelhasználhatóság
változtathatóság
robusztusság[ 1]
A blackboard minta megengedi, hogy a processzek szorosan együtt működhessenek különböző szálakon, és reagáljanak, ha kell.
Példák
Radar védelmi rendszer minta (C#-ban).[ 2]
Kód a MainWindow.xaml:
<ListBox ItemsSource= "{Binding blackboard.CurrentObjects}" ItemsPanel= "{DynamicResource ItemsPanelTemplate1}" ItemContainerStyle= "{DynamicResource ItemContainerStyle}" ItemTemplate= "{DynamicResource ItemTemplate}" Margin= "20,20,20,10" Foreground= "#FFDE6C6C" >
<ListBox.Resources>
<ItemsPanelTemplate x:Key= "ItemsPanelTemplate1" >
<Canvas IsItemsHost= "True" />
</ItemsPanelTemplate>
[ 2]
Kód az item container helymeghatározásához
<Style x:Key= "ItemContainerStyle" TargetType= "{x:Type ListBoxItem}" >
<Setter Property= "Background" Value= "Transparent" />
<Setter Property= "HorizontalContentAlignment" Value= "{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
<Setter Property= "VerticalContentAlignment" Value= "{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
<Setter Property= "Canvas.Left" Value= "{Binding X}" />
<Setter Property= "Canvas.Top" Value= "{Binding Y}" />
<Setter Property= "Template" >
<Setter.Value>
<ControlTemplate TargetType= "{x:Type ListBoxItem}" >
<Border x:Name= "Bd" BorderBrush= "{TemplateBinding BorderBrush}" BorderThickness= "{TemplateBinding BorderThickness}" Background= "{TemplateBinding Background}" Padding= "{TemplateBinding Padding}" SnapsToDevicePixels= "true" >
<ContentPresenter HorizontalAlignment= "{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels= "{TemplateBinding SnapsToDevicePixels}" VerticalAlignment= "{TemplateBinding VerticalContentAlignment}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
[ 2]
Kód az Item-hez (ItemTemplate definiálja az objektumot, egy Image és TextBox-ok):
<DataTemplate x:Key= "ItemTemplate" >
<Border>
<Border.Style>
<Style TargetType= "{x:Type Border}" >
<Style.Triggers>
<DataTrigger Binding= "{Binding IsThreat}" Value= "true" >
<Setter Property= "Background" Value= "Red" />
</DataTrigger>
<DataTrigger Binding= "{Binding IsThreat}" Value= "false" >
<Setter Property= "Background" Value= "Green" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid Margin= "3" >
<Image Height= "48" Source= "{Binding Image}" />
<StackPanel Margin= "0,0,0,-30" VerticalAlignment= "Bottom" >
<TextBlock Text= "{Binding Type}" />
<TextBlock Text= "{Binding Name}" />
</StackPanel>
<TextBlock HorizontalAlignment= "Right" TextWrapping= "Wrap" Text= "{Binding DistanceFromDestruction}" VerticalAlignment= "Bottom" Width= "Auto" Visibility= "{Binding IsThreat, Converter={StaticResource BooleanToVisibilityConverter}}" />
</Grid>
</Border>
</DataTemplate>
[ 2]
Kód a Blackboard komponens mögött MVVM ViewModel implementációjában:
public Blackboard blackboard { get ; set ; }
Controller controller ;
public MainWindow ()
{
InitializeComponent ();
DataContext = this ;
blackboard = new Blackboard ();
controller = new Controller ( blackboard );
}
[ 2]
A Controller mögötti kód:
private void Button_Click ( object sender , System . Windows . RoutedEventArgs e )
{
controller . AddSignalProcessor ();
}
[ 2]
Kód a base class IObject-hez:
public interface IObject
{
ObjectType Type { get ; set ; }
string Name { get ; set ; }
WriteableBitmap Image { get ; set ; }
bool? IsThreat { get ; set ; }
ProcessingStage Stage { get ; set ; }
int X { get ; set ; }
int Y { get ; set ; }
IObject Clone ();
}
[ 2]
Kód a radar module-ban:
AllObjects = new List < IObject >
{
new BirdObject ( ObjectType . Bird , "" , new WriteableBitmap ( new System . Windows . Media . Imaging . BitmapImage ( new Uri ( @"pack://application:,,,/Media/Bird.bmp" , UriKind . Absolute ))), false , false ),
new PlaneObject ( ObjectType . Plane , "" , new WriteableBitmap ( new System . Windows . Media . Imaging . BitmapImage ( new Uri ( @"pack://application:,,,/Media/Plane.bmp" , UriKind . Absolute ))), false , false ),
new RocketObject ( ObjectType . Rocket , "" , new WriteableBitmap ( new System . Windows . Media . Imaging . BitmapImage ( new Uri ( @"pack://application:,,,/Media/Rocket.bmp" , UriKind . Absolute ))), false , false ),
};
[ 2]
Kód ami a beérkező objektumokat kezeli:
public IncomingObject ( IObject obj )
: base ( ObjectType . Unknown , null , null , true , null )
{
actualObject = obj ;
ProcessedPixels = new bool [ 16 , 16 ];
//Paint the image as all red to start with
Image = new WriteableBitmap ( 48 , 48 , 72 , 72 , PixelFormats . Bgr32 , null );
int [] ary = new int [( 48 * 48 )];
for ( var x = 0 ; x < 48 ; x ++ )
for ( var y = 0 ; y < 48 ; y ++ )
ary [ 48 * y + x ] = 255 * 256 * 256 ;
Image . WritePixels ( new Int32Rect ( 0 , 0 , 48 , 48 ), ary , 4 * 48 , 0 );
}
[ 2]
Kód a tudásbázis interfészéhez:
public interface IKnowledgeSource
{
bool IsEnabled { get ; }
void Configure ( Blackboard board );
void ExecuteAction ();
KnowledgeSourceType KSType { get ; }
KnowledgeSourcePriority Priority { get ; }
void Stop ();
}
[ 2]
A jel processor implementálásához:
public override bool IsEnabled
{
get
{
for ( var ix = 0 ; ix < blackboard . CurrentObjects . Count (); ix ++ )
if ( blackboard . CurrentObjects [ ix ]. Stage < ProcessingStage . Analysed )
return true ;
return false ;
}
}
public override void ExecuteAction ()
{
for ( var ix = 0 ; ix < blackboard . CurrentObjects . Count (); ix ++ )
if ( blackboard . CurrentObjects [ ix ]. Stage < ProcessingStage . Analysed )
ProcessAnotherBit ( blackboard . CurrentObjects [ ix ]);
}
void ProcessAnotherBit ( IObject obj )
{
int GRANULARITY = 16 ;
int blockWidth = obj . Image . PixelWidth / GRANULARITY ;
[ 2]
kódrészlet writablebitmaps-ok közti másoláshoz:
int stride = obj . Image . PixelWidth * obj . Image . Format . BitsPerPixel / 8 ;
int byteSize = stride * obj . Image . PixelHeight * obj . Image . Format . BitsPerPixel / 8 ;
var ary = new byte [ byteSize ];
obj . Image . CopyPixels ( ary , stride , 0 );
[ 2]
var unk = obj as IncomingObject ;
unk . GetActualObject (). Image . CopyPixels ( aryOrig , stride , 0 );
[ 2]
for ( var iy = 0 ; iy < blockWidth ; iy ++ )
{
for ( var ix = 0 ; ix < blockWidth ; ix ++ )
for ( var b = 0 ; b < 4 ; b ++ )
{
ary [ curix ] = aryOrig [ curix ];
curix ++ ;
}
curix = curix + stride - ( blockWidth * 4 );
}
[ 2]
obj . Image . WritePixels ( new Int32Rect ( 0 , 0 , obj . Image . PixelWidth , obj . Image . PixelHeight ), ary , stride , 0 );
[ 2]
Kód a pixelek összehasonlításához a képfelismerésnél:
for ( var ix = 0 ; ix < blockWidth ; ix ++ )
{
var argb1 = ( ary [ curix + 1 ] * 256 * 256 ) + ( ary [ curix + 2 ] * 256 ) + ary [ curix + 3 ];
var argb2 = ( aryKnown [ curix + 1 ] * 256 * 256 ) + ( aryKnown [ curix + 2 ] * 256 ) + aryKnown [ curix + 3 ];
if ( argb1 != 255 * 256 * 256 && argb1 != argb2 )
{
nomatch = true ;
break ;
}
curix += 4 ;
}
[ 2]
if ( matches . Count () == 1 )
{
obj . Type = matches [ 0 ]. Type ;
obj . Name = matches [ 0 ]. Name ;
obj . IsThreat = matches [ 0 ]. IsThreat ;
obj . Image = new WriteableBitmap ( matches [ 0 ]. Image ); //Create new image instance
if ( obj . Type != ObjectType . Plane )
obj . Stage = ProcessingStage . Identified ;
else
obj . Stage = ProcessingStage . Analysed ;
}
[ 2]
Kód a repülő azonosításhoz:
for ( var ix = 0 ; ix < blackboard . CurrentObjects . Count (); ix ++ )
{
var obj = blackboard . CurrentObjects [ ix ];
if ( obj . Stage == ProcessingStage . Analysed && obj . Type == ObjectType . Plane )
{
var unk = obj as IncomingObject ;
var actual = unk . GetActualObject ();
obj . Name = actual . Name ;
obj . IsThreat = actual . IsThreat ;
obj . Stage = ProcessingStage . Identified ;
}
}
[ 2]
Kód a harcigépekhez:
public override void ExecuteAction ()
{
for ( var ix = 0 ; ix < blackboard . CurrentObjects . Count (); ix ++ )
{
var obj = blackboard . CurrentObjects [ ix ] as IncomingObject ;
if ( obj . IsThreat != null && obj . IsThreat . Value && ( obj . Stage != ProcessingStage . Actioned ))
{
if ( obj . MoveHitsTarget ())
DestroyTarget ( obj );
}
}
}
private void DestroyTarget ( IncomingObject obj )
{
int stride = obj . Image . PixelWidth * obj . Image . Format . BitsPerPixel / 8 ;
int byteSize = stride * obj . Image . PixelHeight * obj . Image . Format . BitsPerPixel / 8 ;
var ary = new byte [ byteSize ];
obj . Image . CopyPixels ( ary , stride , 0 );
DrawCross ( stride , ary );
obj . Image . WritePixels ( new Int32Rect ( 0 , 0 , obj . Image . PixelWidth , obj . Image . PixelHeight ), ary , stride , 0 );
obj . Stage = ProcessingStage . Actioned ;
}
private static void DrawCross ( int stride , byte [] ary )
{
for ( var y = 1 ; y < 47 ; y ++ )
{
var line1Pos = ( y * stride ) + ( y * 4 );
var line2Pos = ( y * stride ) + ( stride - 4 ) - ( y * 4 );
for ( var a = - 1 ; a < 2 ; a ++ )
{
ary [ line1Pos + 4 + ( a * 4 )] = ary [ line2Pos + 4 + ( a * 4 )] = 255 ;
ary [ line1Pos + 5 + ( a * 4 )] = ary [ line2Pos + 5 + ( a * 4 )] = 0 ;
ary [ line1Pos + 6 + ( a * 4 )] = ary [ line2Pos + 6 + ( a * 4 )] = 0 ;
ary [ line1Pos + 7 + ( a * 4 )] = ary [ line2Pos + 7 + ( a * 4 )] = 0 ;
}
}
}
[ 2]
Kapcsolódó cikkek
Jegyzetek
↑ a b c d e f g h Lalanda, P., Two complementary patterns to build multi-expert systems , Orsay, France: Thomson CSF Corporate Research Laboratory
↑ a b c d e f g h i j k l m n o p q r s t Blackboard Design Pattern . Microsoft TechNet . Microsoft. (Hozzáférés: 2016. február 5.)
Fordítás
Ez a szócikk részben vagy egészben a Blackboard (design pattern) című angol Wikipédia-szócikk fordításán alapul. Az eredeti cikk szerkesztőit annak laptörténete sorolja fel. Ez a jelzés csupán a megfogalmazás eredetét és a szerzői jogokat jelzi, nem szolgál a cikkben szereplő információk forrásmegjelöléseként.