Oldalak

2012. augusztus 7., kedd

Egyszerű "kitalálós" játék

Ez az egyszerű játék igazából a konzolról történő beolvasást demonstrálja. A példában kétféle beolvasási mód van: az egyik egy int szám beolvasása kbd.nextInt(), a másik egyetlen karakter beolvasása a konzolról kbd.findWithinHorizon(".", 0).charAt(0). Ezen kívül természetesen a Scanner osztálynak még számos más metódusa létezik.
package inputreading;

import java.util.Date;
import java.util.Random;
import java.util.Scanner;

public class InputReading {

    public static void main(String[] args) {
        // billentyűzet scanner
        Scanner kbd = new Scanner(System.in);
        
        char cAnswer; 
        int iTip;
        boolean bEnd;
        Date dtStart;
        Date dtEnd;
        
        do {
            dtStart = new Date();
            int iTryCnt = 0;
            // feladvány előállítása
            int iNum = new Random().nextInt(10) + 1;
            do {
                System.out.println(++iTryCnt + ". kísérlet");
                System.out.print("Írd be a tippet: ");
                // int szám beolvasása a konzolról
                iTip = kbd.nextInt();
            } while (iTip != iNum);

            dtEnd = new Date();
            
            Double dDifference = (dtEnd.getTime() - dtStart.getTime()) / 1000.0;
            System.out.println(dDifference + " másodperc alatt találtad ki a feladványt");
            System.out.println(iTryCnt + " lépésből találtad ki a számot");
            System.out.println("");

            do {
                System.out.println("Akarsz újra játszani? (i/n)");
                cAnswer = Character.toLowerCase(kbd.findWithinHorizon(".", 0).charAt(0));
            } while (cAnswer != 'i' && cAnswer != 'n');
            
            bEnd = (cAnswer == 'n');
            // ismételd addig, amíg bEnd true nem lesz
        } while (!bEnd);
    }
}

2012. augusztus 1., szerda

Form TopMost tulajdonságának beállítása


Néha szükség lehet arra, hogy egyes ablakok mindig legfelül "topmost" módon jelenjenek meg. Ezt egy egyszerű WinAPI trükkel lehet megoldani (persze létezik erre más módszer is).

procedure TfrmDlgCommonSyncProgress.FormShow(Sender: TObject);
begin
  SetWindowPos(
    Self.Handle,
    HWND_TOPMOST,
    0,
    0,
    0,
    0,
    SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
end;
Persze ugyanezt a hatást lehet elérni, ha az adott form CreateParams metódusának felülírásával is.

Flyweight minta alkalmazása

A Flyweight (pehelysúlyú) szerkezeti objektum minta megvalósítása Delphi alatt. Ezt a mintát valósítja meg a TCollection és TCollectionItem osztály. Ezt a gyakorlatban olyan esetekben szoktam használni, amikor dinamikusan összetett adatokat kell kezelni és a hagyományos tömb szerkezet ehhez nem nyújt kellő rugalmasságot.

A TCollection és TCollectionItem osztályokból származtatott saját konténer osztályok alkalmazásával ki lehet aknázni az OOP által nyújtott előnyöket mint például a kollekcióba szervezett adatok belső integritásának védelme, vagy az adatok állapot változásának esemény kezelése saját eseménykezelők használatával (pl. ha a kollekcióban egy elem állapota megváltozik, akkor egy eseménykezelőben kezelni lehessen a bekövetkezett változásokat).

A Flyweight minta megvalósítása örökléssel a TCollection és TCollectionItem osztályokból (ez csak egy kód csontváz, amit igazából az adott célnak megfelelően kell elkészíteni, kiegészíteni a feladathoz leginkább illeszkedő mezőkkel, metódusokkal) Kollekció elem csontváz osztály interface része:
interface

uses
  Classes, ...;

type
  TMyCustomDataItem = class(TCollectionItem)
  private
    // itt kell definiálni azokat a mezők tároló változóit,
    // amit majd a külvilág felé publikálni szeretnénk tulajdonságokon
    // keresztül
    FMyIntField    : Integer;
    FMyStringField : String;
    ...
  public
    constructor Create(Collection: TCollection); override;
    ...
    property MyIntField : Integer read FMyIntField write FMyIntField;
    property MyStringField : String read FMyStringField write FMyStringField;
    ...
  end;
A kollekció elem publikált mezőihez lehet getter/setter metódusokat definiálni, ezt a konkrét feladat dönti el, hogy mire van szükségünk. Ha például eseményt szeretnénk kiváltani, ha egy elemnek (TCollectionItem) megváltozik a belső állapota, akkor a figyelni kívánt tulajdonság setter metódusát kell "felokosítani" erre a feladatra, hogy az állapot változásról értesítse ki a kollekciót kezelő objektumot (TCollection). Kollekció csontváz osztály interface része:
  ...
  TMyCustomDataCollection = class(TCollection)
  private
    function GetMyCustomDataItem(Index : Integer) : TMyCustomDataItem;
    procedure SetMyCustomDataItem(Index : Integer; 
      const Value : TMyCustomDataItem);
  public
    destructor Destroy; override;

    function Add : TMyCustomDataItem; overload;
    function Add(AMyInt : Integer; AMyString : String) : TMyCustomDataItem; overload;

    procedure DeleteAll;
    procedure OrderByMyIntField;
    property Items[Index : Integer] : TMyCustomDataItem read GetMyCustomDataItem
      write SetMyCustomDataItem;
  end;
A fenti példában a TMyCustomDataCollection osztályt felruházom rendezés funkcióval, ami a MyIntField mező szerint fogja a kollekcióban az elemeket sorba rendezni, az összes elem törlése funkció, egyedi adatokkal történő a adat inicializálás Add(1, 'MyString'). TMyCustomDataItem osztály implementációs csontváza:
...
implementation
{ TMyCustomDataItem }

constructor TMyCustomDataItem.Create(Collection: TCollection);
begin
  inherited Create(Collection);
  FMyIntField := $F0F0;
  FMyStringField := '';
  ..
  // további inicializáló utasítások
end;
TMyCustomDataCollection kollekció implementációs csontváza:
{ TMyCustomDataCollection }

destructor TMyCustomDataCollection.Destroy;
begin
  DeleteAll;
  inherited Destroy;
end;

function TMyCustomDataCollection.Add: TMyCustomDataItem;
begin
  Result := inherited Add as TMyCustomDataItem;
end;

function TMyCustomDataCollection.Add(AMyInt : Integer; AMyString : String): TMyCustomDataItem;
begin
  Result := inherited Add as TMyCustomDataItem;
  Result.MyIntField := AMyInt;
  Result.MyStringField := AMyString;
  ..
  // további értékadó utasítások
end;

procedure TMyCustomDataCollection.DeleteAll;
begin
  while (Self.Count > 0) do
    Self.Delete(0);
end;

function TMyCustomDataCollection.GetMyCustomDataItem(Index: Integer): TMyCustomDataItem;
begin
  Result := inherited Items[Index] as TMyCustomDataItem;
end;

procedure TMyCustomDataCollection.SetMyCustomDataItem(Index: Integer;
  const Value: TMyCustomDataItem);
begin
  inherited Items[Index] := Value;
end;

procedure TMyCustomDataCollection.OrderBySessionNum;
var
  iIndex        : Integer;
  iIndex2       : Integer;
  pMinItem      : TMyCustomDataItem;
  pCurrItem     : TMyCustomDataItem;
begin
  // elemek rendezése FMyInt szám szerint növekvő sorrendben
  // a min sort algoritmusnál van gyorsabb rendezés is ;)
  for iIndex := 0 to Self.Count - 1 do
    begin
      pMinItem := Self.Items[iIndex];

      for iIndex2 := iIndex to Self.Count - 1 do
        begin
          pCurrItem := Self.Items[iIndex2];
          if pCurrItem.SessionNum < pMinItem.SessionNum then
            begin
              pCurrItem.Index := pMinItem.Index;
              pMinItem := pCurrItem;
            end;
        end;
    end;
end;
A fenti csontvázak alapján a saját igényeknek megfelelően kell tovább bővíteni a kollekció és kollekció elem osztályokat.

String írása/olvasása TMemoryStream-el

Az alábbi snipet egy String tartalmának írását olvasását szemlélteti egy TMemoryStream-ben. Ez nagyon hasznos tud lenni, ha nem akarunk tömbökkel bűvészkedni. Az alábbi kód töredéket gyakran szoktam használni.
procedure TForm1.btnStreamRWTestClick(Sender: TObject);
var
  msStream  : TMemoryStream;
  sData     : String;
begin
  sData := 'Ez egy tesz szöveg';
  msStream := TMemoryStream.Create;
  try
    msStream.Clear;
    // sData tartalmának kiírása az msStream MemoryStream-be
    msStream.WriteBuffer(Pointer(sData)^, Length(sData));

    sData := 'alma';

    // msStream tartalmának visszaolvasása a sData String-be
    // sData méret beállítása !!!
    SetLength(sData, msStream.Size);
    // SData tartalmának "nullázása"
    FillChar(sData[1], msStream.Size, 0);
    // pozicionálás a MemoryStream elejére olvasás előtt
    msStream.Position := 0;               
    msStream.ReadBuffer(Pointer(sData)^, msStream.Size);

  finally
    if Assigned(msStream) then
      begin
        msStream.Clear;
        msStream.Free;
      end;
  end;
end;