вторник, 31 августа 2010 г.

Модификация приложения JobTerrarium

Я предлагаю Вам исправить/дополнить то задание, которое мы делали в прошлой статье, используя интерфейсы.

Техническое задание

А именно:

1) Все объекты, которые участвуют в 'игре', реализуют интерфейс IMovable - он предоставляет возможность позиционировать объект на поле с помощью метода Move(Point p);[Используется для начального позиционирования всех объектов и для перемещения по полю живых существ], реализует свойство IsAlive (только для чтения) показывающее живой ли это организм(например Рабочий) или неживой(работа, прибавка к зарплате, (cтена))

2)Те работники, которые могут управлять другими (Бос, БигБосс, Заказчик) должны реализовывать интерфейс IManage() - он предоставляет возможность 'управлять' сотрудниками с помощью функции Manage(IManagable) - реализация за Вами, сказать что-то… изменить какие то свойства .. вызвать какой то метод интерфейса IManagable - выбирайте сами.

3)Те работники что могут управляться другими (Бос, Рабочий) - должны реализовывать интерфейс IManagable - придумайте его сами , только хорошо закомментируйте, возможен и такой вариант interface IManagable{};

Создайте Аттрибут для хранения информации о девелопере (якобы)измнившем код подследним. Обязательными являются его (фамилия) ник типа string и дата модификации кода. Опциональными - комментарий типа string . Установите его для всех классов ( в какое-то вымышленное значение ).

Также можно исправить SayHello / DoWork / Say в соответствии с концепциями OOП с помощью абстрактных классов и функций, виртуальных функиций, перекрытия(override) функций.

Также у нас появилось новое существо: Customer , он не имеет зарплаты, может не здороваться(а может и здороваться - решайте сами:), но он может раскидывать работы, и управлять персоналом(кастомер всегда прав:). Этот класс не может и не должен быть наследован от Employee , тем не менее он может перемещаться по полю, и взаимодействовать с персоналом описаным выше образом.

Все свойства обьектов (Настроение/Зарплата/Имени/(ОпытРаботы)) - реализуйте через свойства(property), только get или get/set по смыслу:)

Реализуйте (и документируйте) сложное поведение обьекта - например при повышении зарплаты(set) не только повышается зарплата но и настроение. (Можете придумать свои варианты сложного поведения обьектов при изменении свойств).

Переопределите(те кто этого не сделал в предыдущем задании) методы != == для структуры Point, используйте именно эти операторы и эту структуру везде где дело касается координат обьектов.

В качестве ДОПОЛНИТЕЛЬНОГО ЗАДАНИЯ:

· можете реализовать еще какое-то существо или какой-то объект неживой природы, опишите в комментариях его поведение.

· сделайте два варианта поведения при достижении границ поля: прозрачные стены - при попытке зайти на клетку за границами игрового поля обьект появляется с другой стороны и отражающие стены - при попытке зайти на клетку за границами игрового поля обьект меняет свое направление на противоположное.

Разработка и программирование

Опишем сначала интерфейсы, заданные в ТЗ выше. Это интерфейсы ITalk, IMovable, IManage, IManagable определяющие характерные для них методы.

namespace Task_2

{

interface ITalk

{

void Talk(DymanicMapObject employee);

void Say(string WhatToSay);

}

}

namespace Task_2

{

interface IMovable

{

void Place(Point p);

bool isAlive();

}

}

namespace Task_2

{

interface IManage

{

void Manage(IManagable managable);

}

}

namespace Task_2

{

interface IManagable

{

void RaiseSalary(decimal amount);

void MoodWorsen();

}

}

Далее создадим базовый класс MapObject, который может быть движимым, следовательно относледуем его от интерфейса IMovable. Это класс для всех сущностей нашей системы. Класс имеет всего лишь координаты и метод Show() для отображения сущности на поле.

namespace Task_2

{

//Статический объект на поле

abstract class MapObject : IMovable

{

protected Point position = new Point();

public Point Position

{

get { return position; }

set { position = value; }

}

public void Place(Point position)

{

this.position = position;

}

public bool isAlive()

{

return false;

}

public void Show(Graphics g, int cellsize)

{

g.DrawString(this.ToString(), new Font("Arial", 7), new SolidBrush(Color.Red), new RectangleF(position.x * cellsize, position.y * cellsize, cellsize, cellsize));

}

}

}

Далее реализуем класс DynamicMapObject, который будет отвечать за объекты двигающиеся по полю. В этом классе описан только метод Move().

using System;

using System.Collections.Generic;

using System.Text;

namespace Task_2

{

//Объект, который двигается

class DymanicMapObject : MapObject

{

virtual public void Move()

{

int dx = Engine.random.Next(-1, 2);

int dy = Engine.random.Next(-1, 2);

if (position.x + dx >= 0 && position.x + dx < Engine.maxPoint.x)

position.x += dx;

if (position.y + dy >= 0 && position.y + dy < Engine.maxPoint.y)

position.y += dy;

}

public bool isAlive()

{

return true;

}

}

}

Затем реализуем класс Employee, который будет уже базовым для сущностей босс, рабочий и бигбосс. Класс наследуется от DymanicMapObject и интерфейсов IManagable, ITalk. Данный класс уже имеет такие свойства как Salary и Name, а такэе метод Talk() и Say().

using System;

using System.Collections.Generic;

using System.Text;

using Task_2Winforms;

namespace Task_2

{

abstract class Employee : DymanicMapObject, IManagable, ITalk

{

public Employee(string name, decimal salary)

{

this.Name = name;

this.salary = salary;

this.mood = true;

Place(new Point(Engine.random.Next(Engine.maxPoint.x), Engine.random.Next(Engine.maxPoint.y)));

}

public Employee() : this("Noname", 0)

{

}

public Employee(decimal salary) : this("Noname", salary)

{

}

private decimal salary;

private string name;

public string Name

{

get { return name; }

set { name = value; }

}

private bool mood;

private Point position;

public void Say(string WhatToSay)

{

TextBuffer.WriteLine(this.ToString() + " " + name + " say: \"" + WhatToSay+ "\"");

}

public void Talk(DymanicMapObject employee)

{

if (employee is Worker)

Say(SayToWorker());

else

{

if (employee is BigBoss)

Say(SayToBigBoss());

else if (employee is Boss)

Say(SayToBoss());

}

}

public virtual string SayToBoss()

{

return "Hello!";

}

public virtual string SayToBigBoss()

{

return "Good morning!";

}

public virtual string SayToWorker()

{

return "Hi!";

}

public override string ToString()

{

return "Employee";

}

public void RaiseSalary(decimal amount)

{

salary += amount;

this.mood = true;

Say("Спасибо за прибавку " + amount);

}

public void MoodWorsen()

{

this.mood = false;

Say("У меня испортилось настроение!");

}

}

}

Класс Worker наследуется от Employee и переопределяет методы, с помощью которых разговаривает Employee.

using System;

using System.Collections.Generic;

using System.Text;

namespace Task_2

{

class Worker : Employee

{

public Worker():base("Noname",500)

{

}

public Worker(decimal salary): base("Noname",salary)

{

}

public Worker(string name, decimal salary): base(name, salary)

{

}

public override string SayToWorker()

{

return "Привет, как дела?";

}

public override string SayToBoss()

{

return "Здравствуйте, отлично выглядите сегодня.!";

}

public override string SayToBigBoss()

{

return "Здравствуйте!";

}

public override string ToString()

{

return "Woker";

}

}

}

Класс Boss наследуется от Employee и переопределяет методы, с помощью которых разговаривает Employee.

using System;

using System.Collections.Generic;

using System.Text;

namespace Task_2

{

class Boss : Employee, IManage

{

public Boss():base("Noname",1000)

{

}

public Boss(decimal salary): base("Noname",salary)

{

}

public Boss(string name, decimal salary)

: base(name, salary)

{

}

public override string SayToWorker()

{

return "Привет, почему не работаешь?";

}

public override string SayToBoss()

{

return "Привет!";

}

public override string SayToBigBoss()

{

return "Здравствуйте!";

}

public override string ToString()

{

return "Boss";

}

public void Manage(IManagable managable)

{

managable.RaiseSalary(Engine.random.Next(100));

}

}

}

Класс BigBoss наследуется от Boss и переопределяет методы, с помощью которых разговаривает Boss.

using System;

using System.Collections.Generic;

using System.Text;

using Task_2Winforms;

namespace Task_2

{

[Developer("Vasya Ivanov", "23.11.07")]

class BigBoss : Boss,IManagable

{

public BigBoss(string name):base(name,1500)

{

}

public BigBoss(string name, decimal salary):base(name,salary)

{

}

public override string SayToWorker()

{

return "Сколько можно бездельничать?";

}

public override string SayToBoss()

{

return "Как работа?";

}

public override string SayToBigBoss()

{

return "Здравствуй.";

}

public override string ToString()

{

return "BigBoss";

}

void RaiseSalary(decimal amount)

{

TextBuffer.WriteLine("Нельзя управлять мной!!!");

}

}

}

Опишем класс Work, т.к. работа это недвижимый объект на карте, то отнаследуем его от класса MapObject.

using System;

using System.Collections.Generic;

using System.Text;

namespace Task_2

{

class Work : MapObject

{

public override string ToString()

{

return "Work";

}

}

}

Теперь опишем класс Закачик, который всегда прав и который в методе placeWork – размещает работу в левом верхнем углу от того где он сам находится и передвигается в методе Move().

using System;

using System.Collections.Generic;

using System.Collections;

using System.Text;

using Task_2Winforms;

namespace Task_2

{

// Заказчик

class Customer : DymanicMapObject, IManage, ITalk

{

// Портит настроение всем на своём пути

public void Manage(IManagable managable)

{

managable.MoodWorsen();

}

// Размещает работу в левом верхнем углу от того где он сам находится

public Point placeWork()

{

Point p = new Point(Position.x-1,Position.y-1);

return p;

}

public void Say(string WhatToSay)

{

TextBuffer.WriteLine(this.ToString() + " say: \"" + WhatToSay + "\"");

}

public void Talk(DymanicMapObject employee)

{

if (employee is Worker)

Say("Я всегда прав!!!");

else

{

if (employee is BigBoss)

Say("Давайте договоримся!");

else if (employee is Boss)

Say("Позовите мне бигбосса!");

}

}

public override string ToString()

{

return "Customer";

}

//Передвигается рандомно,т.е. может появиться где-угодно

override public void Move()

{

int dx = Engine.random.Next(-Engine.maxPoint.x, Engine.maxPoint.x);

int dy = Engine.random.Next(-Engine.maxPoint.y, Engine.maxPoint.y);

if (position.x + dx >= 0 && position.x + dx < Engine.maxPoint.x)

position.x += dx;

if (position.y + dy >= 0 && position.y + dy < Engine.maxPoint.y)

position.y += dy;

}

}

}

Теперь все сущности описаны, и мы можем перейти непосредственно к программированию самого движка системы. В конструкторе мы создадим и разместим случайным образом все объекты нашей системы. В методе Run() мы будем перемещать объекты а методк Show() отображать их на экране.

using System;

using System.Collections.Generic;

using System.Text;

using System.Collections;

using System.Drawing;

namespace Task_2

{

[Developer("Vasya Petrov", "11.07.10","Класс моделирует JobTerrarium")]

class Engine

{

public readonly static Point maxPoint = new Point(10, 10);

public readonly static Random random = new Random();

ArrayList employee;

ArrayList work;

public Engine()

{

employee = new ArrayList();

employee.Add(new Worker(500));

employee.Add(new Worker(500));

employee.Add(new Worker(500));

employee.Add(new Worker(500));

employee.Add(new Worker(500));

employee.Add(new Boss(1000));

employee.Add(new Boss(1000));

employee.Add(new Customer());

employee.Add(new BigBoss("Dr. Evil", 10000));

work = new ArrayList();

work.Add(new Work());

work.Add(new Work());

work.Add(new Work());

work.Add(new Work());

foreach (DymanicMapObject emp in employee)

emp.Place(new Point(Engine.random.Next(Engine.maxPoint.x), Engine.random.Next(Engine.maxPoint.y)));

foreach (Work wrk in work)

wrk.Place(new Point(Engine.random.Next(Engine.maxPoint.x), Engine.random.Next(Engine.maxPoint.y)));

}

public void Run()

{

foreach (DymanicMapObject emp in employee)

{

emp.Move();

if (emp is Customer)

{

Work tmp=new Work();

tmp.Place(((Customer)emp).placeWork());

work.Add(tmp);

}

}

//Проверяем на столкновение живого объекта с живым

for (int i = 0; i < employee.Count; i++)

for (int j = 0; j < employee.Count; j++)

if (i != j && ((DymanicMapObject)employee[i]).Position == ((DymanicMapObject)employee[j]).Position)

{

((ITalk)employee[i]).Talk((DymanicMapObject)employee[j]);

if (employee[i] is IManage && employee[j] is IManagable) ((IManage)employee[i]).Manage(employee[j] as IManagable);

}

//Проверяем на столкновение живого объекта с неживым

for (int i = 0; i < employee.Count; i++)

for (int j = 0; j < work.Count; j++)

// Работа сделана, удаляем её из списка работ

if (!(employee[i] is Customer) && ((DymanicMapObject)employee[i]).Position == ((MapObject)work[j]).Position)

{

work.RemoveAt(j);

//((MapObject)work[j])).Place(new Point(Engine.random.Next(Engine.maxPoint.x), Engine.random.Next(Engine.maxPoint.y)));

}

}

public void Show(Graphics g, int cellSize)

{

foreach (MapObject emp in employee)

emp.Show(g, cellSize);

foreach (MapObject wrk in work)

wrk.Show(g, cellSize);

}

}

}

А это уже непосредственно методы формы, при клике на кнопку StartSimulation вся наша система запускается и начинает работу, пока не кончится рабочий день.

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using Task_2;

namespace Task_2Winforms

{

public partial class JobTerrariumForm : Form

{

private const double workdayTime=15;

private int cellHeight;

private int cellWidth;

private System.Drawing.Pen redPen;

private Engine engine = new Engine();

private Graphics graph;

public JobTerrariumForm()

{

InitializeComponent();

cellHeight=pB_JobTerrarium.Height/Engine.maxPoint.y;

cellWidth=pB_JobTerrarium.Width/Engine.maxPoint.x;

pB_JobTerrarium.Image = new Bitmap(pB_JobTerrarium.Width, pB_JobTerrarium.Height);

graph = Graphics.FromImage(pB_JobTerrarium.Image);

}

private void button1_Click(object sender, EventArgs e)

{

DateTime time = DateTime.Now.AddSeconds(workdayTime);

do

{

graph.Clear(Color.White);

redPen = new Pen(Color.Red, 2);

int x = 0;

int y = 0;

for (int i = 0; i <= 10 - 1; i++)

{

x += cellWidth;

y += cellHeight;

graph.DrawLine(redPen, x, 0, x, this.Width);

graph.DrawLine(redPen, 0, y, this.Height, y);

}

TextBuffer.Clear();

engine.Run();

rTB_WhatToSay.AppendText(TextBuffer.ReadText());

rTB_WhatToSay.Update();

engine.Show(graph, cellWidth);

pB_JobTerrarium.Refresh();

pB_JobTerrarium.Update();

System.Threading.Thread.Sleep(1000);

} while (DateTime.Now < time);

}

private void timer1_Tick(object sender, EventArgs e)

{

// g.Clear(Color.White);

// TextBuffer.Clear();

// engine.Run();

// richTextBox1.AppendText(TextBuffer.ReadText());

// engine.Show(g, 40);

// pictureBox1.Refresh();

// pictureBox1.Update();

}

}

}

Как мы видим реализация системы JobTerrarium в соответствии с концепциями OOП с помощью абстрактных классов и функций, виртуальных функиций, перекрытия(override) функций оптимальнее и локаничнее.

Комментариев нет:

Отправить комментарий