Blackboard programtervezési minta
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]
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>
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>
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);
}
A Controller mögötti kód:
private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
{
controller.AddSignalProcessor();
}
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();
}
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),
};
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);
}
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();
}
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;
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);
var unk = obj as IncomingObject;
unk.GetActualObject().Image.CopyPixels(aryOrig, stride, 0);
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);
}
obj.Image.WritePixels(new Int32Rect(0, 0, obj.Image.PixelWidth, obj.Image.PixelHeight), ary, stride, 0);
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;
}
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;
}
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;
}
}
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;
}
}
}
Kapcsolódó cikkek
Jegyzetek
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.