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.
Ezt a mintát a HEARSAY-II projekt tagjai alkották, és először hangfelismeréshez használták.

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.

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ő.

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.

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

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

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).

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>

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 
 Programtervezési minta

Jegyzetek

Fordítás 

Programtervezési minták
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 . Ezt a mintát a HEARSAY-II projekt tagjai alkották , és először hangfelismeréshez használták . 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 . 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ő . 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 . 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 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 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 ) . 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> 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, ; //Paint the image as all red to start with Image = new WriteableBitmap( 48 , PixelFormats.Bgr 32 , null ) ; int [ ] ary = new int [ ( 48 * ; for ( var x = x < x++ ) for ( var y = y < y++ ) ary[ 48 * y + x ] = * * Image.WritePixels ( new Int32Rect( 0 , ary , * } 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 = ix < blackboard.CurrentObjects.Count ( ) ; ix++ ) if ( blackboard.CurrentObjects [ ix ] . Stage < ProcessingStage.Analysed ) return true ; return false ; } } public override void ExecuteAction ( ) { for ( var ix = ix < blackboard.CurrentObjects.Count ( ) ; ix++ ) if ( blackboard.CurrentObjects [ ix ] . Stage < ProcessingStage.Analysed ) ProcessAnotherBit( blackboard.CurrentObjects [ ix ] ) ; } void ProcessAnotherBit( IObject obj ) { int GRANULARITY = 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 / int byteSize = stride * obj.Image.PixelHeight * obj.Image.Format.BitsPerPixel / var ary = new byte[ byteSize ] ; obj.Image.CopyPixels ( ary , stride , var unk = obj as IncomingObject ; unk.GetActualObject ( ) . Image.CopyPixels ( aryOrig , stride , for ( var iy = iy < blockWidth ; iy++ ) { for ( var ix = ix < blockWidth ; ix++ ) for ( var b = b < b++ ) { ary[ curix ] = aryOrig[ curix ] ; curix++ ; } curix = curix + stride - ( blockWidth * } obj.Image.WritePixels ( new Int32Rect ( 0 , obj.Image.PixelWidth , obj.Image.PixelHeight ) , ary , stride , Kód a pixelek összehasonlításához a képfelismerésnél : for ( var ix = ix < blockWidth ; ix++ ) { var argb1 = ( ary[ curix + * *+ ( ary[ curix + *+ ary[ curix + ; var argb2 = ( aryKnown[ curix + * *+ ( aryKnown[ curix + *+ aryKnown[ curix + ; if ( argb1 != * * & & argb1 != argb2 ) { nomatch = true ; break ; } curix += } if ( matches.Count ( ) == { 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 = 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 = 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 / int byteSize = stride * obj.Image.PixelHeight * obj.Image.Format.BitsPerPixel / var ary = new byte[ byteSize ] ; obj.Image.CopyPixels ( ary , stride , DrawCross( stride , ary ) ; obj.Image.WritePixels ( new Int32Rect ( 0 , obj.Image.PixelWidth , obj.Image.PixelHeight ) , ary , stride , obj.Stage = ProcessingStage.Actioned ; } private static void DrawCross( int stride , byte [ ] ary ) { for ( var y = y < y++ ) { var line1Pos = ( y * stride ) + ( y * var line2Pos = ( y * stride ) + ( stride -- ( y * for ( var a = a < a++ ) { ary[ line1Pos ++ ( a * = ary[ line2Pos ++ ( a * = ary[ line1Pos ++ ( a * = ary[ line2Pos ++ ( a * = ary[ line1Pos ++ ( a * = ary[ line2Pos ++ ( a * = ary[ line1Pos ++ ( a * = ary[ line2Pos ++ ( a * = } } } Kapcsolódó cikkek Programtervezési minta Jegyzetek Fordítás Programtervezési minták